From 4444ef93965e462119857dc8e7527456fd37a914 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 31 Jul 2016 14:39:15 +0200 Subject: [PATCH 01/46] version bump --- doc/json.gif | Bin 450319 -> 450236 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/json.gif b/doc/json.gif index 46f005dd0da32ad9a2723e34edf4a8219699d118..62a1a2eb93de1c62c23214006a154c28ee043348 100644 GIT binary patch delta 12053 zcmWNUcRbXOAICp8-8ppjrFHg^y+`WoEh{UXBYTA;RPJ*FXLh!Ptc*xzNObmUoZUbY zLWPFX^80=Ncs?HQKi~85e!Xt?9(mkzghiq>T4>+5(Qh(UZ?e#Cw$X2~KU05}c;C_Z zzK2bNt7V(BNt>(1Ll5iD^EUTM4((nJkG$*~FS~L~_l;1#1&|qBH z2<1lPXhIYSQo6ybZS-s1iPs0gE2Gpa1Buthlj5JIQWy+IdU`rDIhl3yW^Qh7VL`#| z+qVk~3tE}fwwuf+tmKZoo839-4{zo66y!*xK6K-QC^O@o+GmK9R|I#!4E^rKC^hrA-xP4&Kfkxl=e(k~>~;d$6o%wzB9| zb?IDLeqVL@SWWrFy{hrXx`Fzdxw?wi^_8;^Y8M{VK5uQDX>D9=sUK)>ecJVKptonX zqxp4r`+WDq<&LIz-E9->-i7|&)q&oDfr0U%p{GxuJ{ujG9vyi*K0N(&;`PMnOU{ds znVFe4(^GHWyjgfTzc$2P9UI)77+HBfvH5Ilb@KVgspl&*Q=ew1KEIlKzqGiu_nffg2ZCiWp*2B8X+AQcq= z;20eNr4olEhTG>oyiLU>%x0_=dfa9vI^KPqu@-t^cI0zgVjVyDh$C;m?YjE>h;&!; zjN^_`V_{?;jEArb!W}Gpc8r>O{Pt5@sfy0fY@%5CvDugPAefeq!cCl8IqPzroc{}n6X?*wZ<5}=dyK(0fKQt)=>Oq%p@Y~ z2xr`u;Z+)Ebhn_V!%BJJNUKYu=W@pi(tfcDk$6H1{VJ>e8e59%e&4kn&Os?_&CjV! z_rq>JWOBXWAr%kC+kFOQCw1$NQ^Xm~E)9LxSQ zcLgDH_Ymo$*;GA!ifm+#qlw~r(nOHt(b0dXOw6soKr-Nq9w?<^`TFWN=;T_L)i<{~HxO|0b& zlRfJ>zsZwr)?gIK=RwC>>u(b!nRtZd$NV z)`3j8JY2~@RYbF!}Gr18j>P_R+bO5ATIWFVLi06tq3D4HLYJ#pDRmN?@&h1 z#WF*^NQ>EbQq(MWsoj(|Y7~<{8}2^zQx$4M6O=nDZDhv{1oU^*nVDF~ z9>;q@?8#lb?fqvUyKjm;BJ#KlizzGIWPN&-c+*EH`;>u;C6$J+>$ue$Q zPH&j%P|uM9;lq<2u*6Pl?9OlOBh;@uN0UcW=tdmUq0h&lzqO5ntJ5p|>K}w@zSM_~ zy5ZYZq#q;kXmo8FgxA66=W(*KUG!x@Sg7fMI740?RV;hhi;tW^VG}hdu8O>Zc12XQ zqxqh#!-e@=qnQd8@`I6e+@VcBn&!%jp$o3!=D@8?*}?bJ2=(A=F3ek%!%OtoSIN=_ zU)CDyHm>wseX?`ApQyHf8Wz|wGbsiN`PV#4Bsf6!)QOf8O*+j`5YnLNdt3aGr{=!L zg*ZFnM2vPX;MRfZ!vOCScvi`>?uD>WvQwK)#GqJj)FXaH&)@)?L^ttZ zPdla1)O4BFX<1nM+h`Q0 z8TJZ!8hcCDc5lh~miC8|0ARzGk+M-zh`{u^_3q}l#ftEEgSu3HKv!9aM~HElo)w195OF@&Sx#Eml%_3 z3?R#Zf8VYO$silq=;s|uH%742SKSXouAY>-ikiF@(}91gjr~4-N$vyY4+GF13X6*| z3;dvGN_prvGvz#Pjy<~?4uw#6vw=!U?DU8=?q^6g8~s7lAO9W@y&6$7a7jPMs`{r% zgKRX%DE5_v57xB~<~a7NHP0WJ=1sR8yJw7r|s(fzP{eu;cvXopnM@tw)nnE{QG z=3tEK&*CjX_FM~!N^pY^4G(g>ZDh#Zhre&l;P&DGYy zn9)nTiWqhhOuF_zt;Wdrzx*L1bHDmsz`CMSeEDrE}XfemMdV`9yq*MIg z2WBYlpI{M8ztrfUiMoHH0qt1qaB|SZhS?Q@#phfNLmjNK8`gG6=^0lFO#d> zgghL>Qw7pyFnn-BwQrOyS}N2qE7IKE!8lzwFd%T_^Ch&w#EKzk0j)q+KmdQ8wi!apZlzHu@Qa%qYR~H!VLHC zaDDXshTm*vyd)_!+L!-TOe5Crn+E#0oT>2ljEAUTD3vMag8e0H#h093dIboN1L_!Y ze*?@b3ePx5=BJ8kj@M$P_XC^pIqq%1562uK**vc794>D#2?^-B#!_^+0&-Mh;i}ii zwJzbmryTOO{ios0rD8z&5Pq^N&H9&Ljkz%dL{<1(X31s2yr~0jSds-mdyjcvqg279 z>1kqRx(ow#3IJ^yqM7lnC3~+1J5UqDPra@6E5z2gP-y<#y z;I1XMcO@?+OXx>Bg|mtIpAL4}JrXgQwkzTHOXi9|xe09(!$cvTi>CE1=c%YG`?7ffK!$4p0)NhE~~P_q0*sPC0E7+J7O98xlRcypP0FhaTe3%$74;M(+;fT!;t)C znI^X*DIQop_dJ+*`TLwQ-DnfuVC6$w*bFhn;sP}vBBJzn-5qX(k00Ow+`Id`PE8A4 z_UN7qwoIt$UYXT3v`_xu+~m5ds*`dBpK=0ZHcAAH%hoiD9tL>{R)_R;)!J|=U;7>1 zj>Ehtj7{(`WF@APgl=0})DKx!#|wrE#}>T&O1b|w7?C(zWW()!_IHGX8Ap)M+<|zt^(hTQXx_elSYg#qQjrp@m#w3_|}Gp zNYWauf+GQV=iD5WjWwS;`{Ly-t3b2FnTl^d-usWR;rA%H1nrAxvW2)+*ljs+(3t_6)kDoL>=b@D@nlZ{<qA6BGeJc3TQi4@AX-5C5l80L?Izs_V3Q&Kfsx5DnO~oLk`tN{nwc zg4F`%#qJ@GM0I-|A3n1Eb~Sb)R4n+s z{z)H!%PuG6jjoPW?)U)C9HY_BHpTkW1BdTosl7vzh~Nl2A(j%dti&)WMNOao>(*mj z&DCKmEVnkm1%{+brWzE+SZqAVr-YDBFtE?j! z`bhqN%ZGTeb*W7Z;2|P}B0@J(oMPaRAUwK4-fFr%Nf++T%gtin4z#s6jhC?!8gCxQ zblUmjM_%a*17T^mB7J9v2j7S8hzuC*R~JoWZ|dg!@L?T{{~nn1$(p9jSB+Y92_4bB zSaVgOJy>is=6AqY zs7Wp{-KTBj%Hc|Dld6hqV`wxTQ|P7 z1MLvk(;98^zAqCspp{O+!~dQ>{i=J=xGP_To%PKbRo*5io&G;eyGd8*s z#QJ84_0^W(Zu27=fJw(QKjX}b86)^ZrHcKB0K2>KyiUH@@C)PmEMq|9Y1mu?e|b7y zlsy`6%;dSRC!~j7Q7~t__j>$d%gIlmntk36j3cuSYNSP8?9wPe!N(gH{9ZhnQ)|X@ z?vAF`YkLQaJlPqD<9+(nFRk&#DgIL>S30l9n}1W<`Pw1;#h$u1qnUQ%L-rWY?xde7 z;{COV{SoX+z$63@5&C^adf}oYPt8ZqslNY;zdMzp!|%Vz*3gOS*1Bw*Ym$BnaM(3y zBsTNl(Ju(C*B13D;V;$Hc;}a%GB&OFn*qdVF~E^i=*%M?=J!mwi>xQ$nE<3t2;jmZ z>POL5JY!DZYq}$j_+bzNpRzL-4CR z1@9{J!XU`-Jg-4Dg&ftr3f1>eejz)h;$qbtdv%^=6%l)+mFBy?Dq7#5nlRsO89^I4 zih)wQy{bU2%D=^TJW6Q3ZA=LkA%usB;E~!oD}P!X{D=_Bs){HJX>?Axi-Y-HftAQq zRg+qgCGqzQAow{dd}ZnpbJ9oY{6eRN{&Nme$z8TfdV5(XWN2k`gOot zQ^4^xCWH-~kOX)l?#dZJj(mDx^G`$+1Qo?019k%4L#LYW0(YKcOa3#b*%W|__*~b= zUPi~%;0?x){n4w+@Zx7h2i6*D^7Q>}?1^e11g%^A*{-t$lWu!@R0=(m;5Uo}{KsvR z4D4<-Vv~<-pZwxIrX+A557~j<$l{<;yTD;3{t}h}5?Hx7kfj-QxnLGuob%bY)l_P( z73*L=T8}Lg%w5Vp&>{bzm~IFtyDW3JfD83Mf{jeS3#hpuBy4WUT zW!T5dasbrVUMg5J_I=cOX}_=+g?c}NW_bAhs;UxxS_^jU@jE0E=P}n!00!50#C-*~ z=rc&FzFuHY(bnkGO`#k<69^vYJvU*8G<-VipR*~@d>gmBUb>WVscnz5n6 z4FHFH#nZ2p`8;4AJEo??;gFS)050Ntq2roWzT$;m2Ia8Rx=oR0*wSDM=NuHU=ZTrZ zv8Beme(a_*WZN=;)_kd z2Imae6j2JzrWx8H0Vy1ooZ$v1>;Itr)({yU?Db4ZpY5E6BGQ~BNUtXH+$VaKfCCg| zlOqgofbdC`ad6`6$wBFkojiH%RiutQYBlY z0)I;9KUkfRE_fZ^?31mr{YL@(K%xlHB~aH>I4TU8mI+a6eCnw1lM0m$by(vOyslTT z1UWmip$U*b$y!N+05`^70I)KqDXoKV3jzJ*k_sIWRmP5$YaIWtF&a{l61eU{}^ zrR*mQJO{Dzv^T-uOhx2zWbz0-N)D}iGrqj+5osnCgxDrLBGUet$sbT)P=pfUd6`=S zkV11yP+zw7(&+-Iuw4xcfl9knX>Qc|)8;a_-ZuQ%WGMg-sK)67kk(QkJk=B50s?}r z^m_zAVma!Rt5>3?i+jM{c1Om(!jd-IJP03 zbqpm%{h`McghB}fX;VQ!kQ*9)>>IS4&ke8vxLary|HEOqwdioQ<@3yj3RLGhU%vn- z=O}-cMk-Sw)`~pPA=Yl0wGeBm9wyA3>;p9rTDd|w*fSL8_ zAn0bBD3wWW`EERLZ&VX0#WgwC+$EGPQ#E4a*))~+bNbWNI-Rm0vEi?S9L|_9Li+*LtjZq5!ftqGzZM2af(=8PmU4XHT_7OO(Mm!umkn;D| zYIHcv8)d4~&tEMLNc&7Ua0c4S#{9HX$7lGXyp2S*Lt@6L=Us}kMH}K)PeM5*VhJLG zVf=&m&0odF@qOsQTzCxU%LF58p!5L&FS@CA)Wizev~d{eb;j`=G&?8DhO(JymN0q8 z+zLsZRrE7&s2qixnGTHOOrU=)jfIjnFg6A!B@Dzy(|mm!YW!rSzo-)LTFC&4=M*x| zPV)+*SV=IqaC_t1ub$s~pV}5!qb!JFL2+b+|qU){GsdXQqV8&jU5BQRlre^E&rny^f$EhH0+37AiSLa zR~0GW4JXWp+L($|Cz98F4?SH&@F>6>diK zL)=sWm=LSz%vhovw>=w{nu|$3H<&&Q4{;mPb72M^0$fzy0d0ci72)gOLN9sx(6N6=s9Uj! z2|iAL3=+!o)m@x)Bde4m=F81ZCe0qrt_O(uMmCmq;e-1*h5dPV;vzRLjc4D_ew=HR z7KKQ1vy?|G`Wy2ET6FNlN%T0T$+Brx<2+x5jVTE&ne*P_mKMQN4vXh(3#}APWW{2*yUnqwjI9j@pp5qZ^7-&CBcHwjV;#3?MN)guo*7o8+ z_$#0;Z+!A-wy(V+Ynol>C0X?i*;ds@oC)$-F9p5=x=ff#VkS)O<3lSa9QVzT;p zB20kyvhkk{Km_GnVAfTXO(7`781*>`5$uyzB8;H4Z=DIo@3AoG2ip*rFR2+4o6P-< zyD;fA_oK5I+s85`n%b!N9H-B+C)q0_RGVAXQ+Rcakn_b!yxsS5)9&JT@s^jFHh&8-pSqEC*hG zSN*hC@kFjk-wiHm3t)8}Ya+Gj)VQI36UCK9`1EM?30K1IF`qdDF%6Yq17<>rA{wGl zkaH`Z?`4hM{DlFYu-~>9MGupWKF_>diHoyWW(Yqck;kP(9<+Lfh6C5$1q5CiOEc$f z>3DXfN;skJIrodAS_Gvwa1c`44SY?j6GQ>f7QzO<157lPjQjKz?S1RfNh~+pQa|)J zH@C)Vbt~AxBOw5r^I%z)<%qKz?K-Z<)cn@@eYcGoat6|d?Na4F$wWK>a6~APPGrp6 zS76BCAB&q~-}r^=3)80=FbfU(ay`IPco)}pi=Yt7-%Ft6_W!n$Ta`Gt#fp>l^uDKe zkvs0Pe6D#ajhEiB_`JN z{?WSVTaU2is766$NZ=VuXl5Pf)-msbx=!_hP;eh6O>+TqS5KWAK2wE>sKyjl&F=DNcz4IAWe0>D2J4Ok z5x}jlnEzzpt00uC8_@So<)khY-J%b8tEe{1W-<|6yNX8i$MnMV$}(k%B0vu8mopiO zK3aV=s>NB58?oH^>vFZ+pJEb|26G#RZcyNMgr2uRmc;=P(ErkAQFw0{rO*(G_c)@LBf8K z+p;@KFP)Tu{eXMt&Eg zSeMdKY<>m)jH4h!p{iJ34;pDp8#*{{Iib~sNwx$KwYhf0|I!0qrX$L|K?sc&IU`sk zU*t=r!Bvj%_G9v?ZH2E+8&t}r7T$#rPOZfPM^sZGv4HClq2NIjYrkxkAZ+3E;CdX$ z^)i>#2|z4BL=)i7vYjcs*VP6>1Fe@gj?_bdAGi~gR1|<>g0R?JPZj{hO2?_eU_=}e zt1}@5cKZU31M=UR0a5E*Zwz?uNS;tPSoh^#GU18xHG<1!r5v48!jXw%p}iAEDUb|# zR3YQ`)?s8U7&su0CKM4+GInZ!eshjYuB{nV(8CyYXYd}JDX+TxtT0vpvgp3BTsdueAN2UG3DU-Yb~GklU<2$$~! z2%S>{>`}~r%G_eACE7`z$#{n~z|`9$k0LA!84X#9YX8bw6kvNRX(x9bk8i?No1lv4 zc&YZPc4bg*5Wp~L%BKBQe{=09Dlp5Y+`}!jMGQ;L)^W*#uC@UWTTNj(=5I%)>uw^{ znC8td#hh+%TVi1*IP)(jVNMx|r>tPVQxTdd7Zfi`1AkX~T+ptQMj*`PF%vCWaGCKG z4YHQZ+FbbFTrHp_mNI+kZpzv0Gv3?^XVX{;Qu8ra=C3BY=Bt!*YALvw4A;EaqD6^n z%z;UIcvj0b#7j`vjq^7Z{i}lO@XIsuDKbLU+2otOyFHA&mnnDM%)VuH=#&~ZyaXLA z{Sf8pI8eI|$G7-0Tk}2CY$`k~hjlJ+gk*A2Eup3R_I zzQ1mkesg=M7@>F`D{&S^C~dp)rlZ8om36K&BacK(yj=C>@$8$PwKu(cZ=N6)*uo2a z(hL2X3j?MLgXb28{1%3^F1~JGd^5DTFuS<8wz#yH<0Si+;*xSMFwaBf zy~snjXdulgmg!y#I~>cQu z$Pf~pBIt+MfWa^*UWhQ>UOtP1>VORAWu^z2ZWiuqw)9?b6m*vH;|+x7ZX})q8TtfY z+x%q`5z4~>4iPC-iB6dIF36*@=IG*QIQQOc9n=NkddpP7vQ;hic@LRo2OWsnHW>WS zKbW+3PRak=I>0vyS~ac;S|9=wmPxhr;5jhl;kwUre@8pNa|iyO2?4x#cvLu5E*NusopO9V+y$cZaP#lyMB|s>mE)VGBEg|{0jUqyOXe<8<}LCDEh?u3nd+aKFy)<+TocxqQE{z36Fp?@+EiZ8e757T{+q!`A9(fwQ4k_T+4qAc z2VnOZf0W+ZVS^3K!vAL_7j21JJy- zKQ+Wa&M{FgqZjjk1RLR^Bq)?6(lU$>`&s5mI5TEdRtJ&WgfJsgEAoR+E%Q6z5sWf= zgUG7jC|pJKtpg`O-PI2!8^N5rsBO>dK!i*lfO^`fQyTyln@Hbftcv&wG9ym^Tq4;s z6wiL-BS2&UhFKt7CjwGe8fchL{hEX_BYwDQMzu-c#xr+xbSNmtz(BARVnL3ynO{2J z3A^-sH&~12ewyhXOA8_S>1w0S18_46xA^E69s-0&fD=Kw0Nc+)8|4tVv6CNTxF1E5 zB{@`okI{mj2T|wow2CV>(hmr5U+vv<2Y`hR%5eE(oLPikCnB0B(; z@WiG-1m#B!k!i`b-jJ}DMSvQ(#CVYt1!6Z2NPgmM1bsP?dFkVQJ3l>a?ABVM8waK) zyCv~*j|9T-fed<6;wfaDDDh{&%^it|HNmT)*Kd9g~36rDhqC{yEpB6Qem(q4WP9k)11=8qh^H7Qo&1=-&l_YuZ8s<&By zUQ?nw8KGC}F9=@o0{-NhLC*b5WQuN;{!BDuUxthTqP2g_IGZJn8;5Hd5ofNw9STsw zhjb`G@I&oH%?gA8?0;Q`yx32}7J*X{6C-X=Op<I7SYXj z_UN^-hU*D4|2xP-qKLi7AG8667@wp=m8b7U**kBvBTUw@A@GaDaJraw)TfOctT&Dl z(=fa0om|X!;_Ii~s(BM_-Z+1PJY#-P9H-KGEAZYRmkw(;mqksJuuru~jha1RuZlct z@)^li4Edq(x-EFVCG7S?QE@&4&FE3~H}O;_LGc?L`x)_hfp{*pDbR44XdzaL-$S4* zT+*YrMHcNx71y5@XKoJ|O83;gm01z1j7sOsmXP zUqoD)Jic4;N6VFQUWZ0?WLo(4U)}E;Q#DQ_3m3i5?vPLf8OL9^Dh3n4Rcq&3H7EePznzdHWUS2MUiM6C?u;d82 z0GvNU=p97rvES~rY1~XAO}3}W(S*xL=$#R|?aJOAui8yk3QhBMtYqea?v@2!x!Hz> z;8M|>F+48RY{Y%iT~p$vNye#FGex0yv=OeDp>-Pin1Zlmfr;V9Jklx}I+y8J6em*@ zbT7qGsU+HvRv;Ep%P)ujyH3Fz^Xvl6B{MkUgT^aiL9Yv<5x>2VQnAfeN+Ei`2~}BE zsbu96sqzcR^uR$Yv@F4g`y3z5ArZl&H7#IRaYXr{9)8s^>D$-UY@2tRB3+;I{SECteEUdw--JMX zE1z%0WAzsXf71CCPEn$}oa&6TgCmCc`z!od8IHGa;`?JIQpEVd#98RKS zqsXpV7q+@@d+wRpC5MYlYQ+uw0h7sT75@GYJ&Et-3>_%QAOAX9#{0 zrz|wQs_rd3iq2d0d9>B@=ipC3?F=XK?1hmHjo^f|8>b%8MKuqM<=EC|4E759Dh59N zt4RG@cLL+tfC+IubNbiX#OnCC-mS39!!1|i{+5f35R4Vp-%!pRNDk2khjvJ%M_;o2 zyB*3gFx`n0P{CR@C1tRmPaOwA6N%8S;?M58-vr)4YnrX$_Ho0h2J8M!F3MWuB~6V zg>dXpC1GeF(TGhur|UmHTyuRI{5SZ;w*?YWt0KE)DHCE4!KUO%O(O z?G!~w;0d(Z@|{;H88AbMnWM4hBZdM3K0Td-C>=IUDte(08`5cr>=LaND=x=alBu3% zAlgu*C8zXhz5<^&(=wu8xH%$G$^+ZUzW~BVuk}g$Kn5*w{g~^!%d(g2tV2};uL@-i ziJ6h5DSZ2E{t}%5-rNEz|51sPS!t#UE1eTTj;$V&ZFEmJcCwwxw!rz$BdBoqXOY=C zr2D%x5gCU^!^i%xM6T2Nbm_t0fT}?zKNZ?mhBP1;3Etx* zs^wpEDq5X{u?O=d*3RUZE*K(H{DpUTQ^tCBI0vvRT;1_8Q*da{E$ V)RDdQ{|A{r`}_a^ delta 12098 zcmWNWWmuCB7sj6@3u250V|2r4P+uH9B%~V!Bu42#(%LiFaP;U9bRZ(qB?5wigh(h2 zVxfeTh+tsh`~UF!c)pzLzR!JM*Xin($b2NhK0|9D>(yBuZ#2_tBir2Ixdr+DUe@qA_JOvR1f%Ccv7%SNgzhU@S3*W8_}xxG+b zKHX6DrlD$#)?7c`T>GZErtf~sXlGmBqwcZCT{CS>3lG~SA3j)YYgl~H`2Jz*hez$N zpL8$xJ?iW08yV;y9UYx|_H=4=lx+4|Vf z^2FHY6&05EPW0m?ADcL(mzIT$g;M$B_ZlsnvVM;^T2%^eYV zVJ`8hm089Qc8)9AZM!Z%myqpf7;^Y*T$>ir3*%PU0pX{~exHr&3%^|G;XXlyh!5++ z)!N(EuxiFj!+xo%4fByy6X!0hdMn%~k~Z}~{9@YYr%0PF&32W7KQjZN;NWWIFSVo?I1ZlBK-2XspHl)o7zGed|we0Uh)846}bX6n#0&( z`Q?ylhw323uPeTeW1_|YWlZvf&K#E=`~Cqr$?5RtxArTOVV%+f1jx9J14owzN7+09 z7o@eocB8@*ygxf}*5~a6*<7Ic2s&+4)l|fb7lcH;s)+k|OTP8k@^1aK-kAN_m0<+q z?0I0;#-nlHD7zxOvy;m@5-0wIk1re=c(mVOSr{~I7R7brI~O2uHtCaAcfGD4S=px( z`uc|U@vf$G11T%RFkBA;q;akP9!e9bc=qknt@;kN9GP2>p}z%&1k|pB(>%*U9}?%* z4oRUO8!1DR*bK=J5AdJUw;$GHy{%@r8>FIOM)zl7{ zpfw(&47B2KBu4EEfa{>=E<}dJF@0(^kk`o>8L6#mT0bs*)~rca@>U(H+j`pXvkH4&N;2f2%0l?1hSii#Q1%Hq7`~5vZT5b z7$M2oO9vzhf&?JEI5#_enCB!yyS!*u=n%MQ53f$st4DHp9$tkoWr0O_fvzdO9u7hQ zWt_EwuoQ~v8l*949vAT42r>3S#;c?IIvKcJ==EW!T*Pvv=||e(flb|`a~8qfp=q!V z@2F4;Dy`|XKfI737Z_*3@Dc`QOVn*{u+Vs8TR_;2X&h;fh<}m_oYj}5p~7QuPuDYI zks2o7XAuB;%@||-%9r;ISKUbm$xc41nnaV{WGW@0=C3I^cIjBzPWBAlGS|)G&gDL5 z(!4{l)J2mao<;mLI)11QWLfSStzmL+-~+>{}HJ(N`frMv_NN*#=h zQg9N7$Mkb9j%%Jsa)_CEY$~1Ybl;1C-o{&1Mc9#Ot z-@|I*=2*;Hc|h;o8tb`OdOT&Vi7a`=!n>jU2~|ZRxz6nBdKnE zvZj?p1goMQ#m_O5QK$b}nD!DB93 zaC?aKMF2TXb9PPmrURS?(H?GU$|!($#bbUIx9|g(LuDPX3R!_WmnLP=$t+ebjm_ly z#>%X^3=j~7q-53*^N&4Cc0P=p>+UGS_?}B2e-bz#rT(f%w$`#t7_6yWiRQ+g&l&;GmR{OD<6;E ztTOY|J%J_~jK=|pAY9dW*rmX@T`AT`i=o{ruF(xwGpGh{3JKT4c_jVsH`yZVHr9uI zsUsi^&Bpf4Q+TVe#V%Lw=VYh5sEw1;xljjY@|NJn?$w8_R=uq>@8S8#S8MdWNg9Al zMMKg9r*ZW_iugy$$lZv7T*9>=Ts;m!MypPXUelDrsoYarm1n-2Oz~?Bc`pj?n&Y-& zd_f`W`Z)7{L643V!nw0~`JS}sX5T6w+|xz@>NY&#NBM)U_<0E~)+HTktBc{b^3vK6mN$NPPq zVl-6gN}DnE`v0e_e}NQF34fW@DzBR46I0?;EcX)(f#v+2OYg+dcJQf>-WT-_2QFv z)$SGLCH`dleP;yfM!cR%+etV(D5`qb^_H>h^~xV16P5+q7qibUlI&w|jz?zY-PLt* z$|nfw{1P)aiUFUy#wt_H{{@yBRp^Rr;2IiBWh<`1Ave==^O43keNBQ46>&xgb+)3b zv_NSYO^(Um$6-s~gz;zPRYt$QeRDkFtXNT$zi{1~vZ+q%cH zTo~rsTIyRB5&ieDL$08!JM+ZX3qT<*n;2sQi{Z;EDF`kqc&UA&^XTbkI0Vy0MNhLW z9!Fd&p$_If5{{d|=YObDJGi5ZtG|AG;%?F_T5JA>=53sNl0vKjKgTR2<_d4_UD(7` z5%-es>n@f1bmQWaA|VN^g`BT@IlP=I^JMnhU4+9VLZmb9uZaJZpLK4@K~%SdkUd~Nvf;-mgdb6Tqqq(sS@|lr zy0XZLxK9_=sBd8M2R^Oi&bC585(|$x^|}B>L|o~ELK-VLDHhm+rel^F(lBNlm0El* zS!Vv$_=hS0b8Dsi;Pz9B?#E+Sp>$fUj=%a3E_E)P#)$l#WRZHPWFh|u?(7EHrnT%` zwQfK$ZbSq~wdB_ewTQR4y^-yV_QMA50FH4aYIF*hFA5P{a4DFSfCffAg5R|>l2cmj zFIM)M)V*`Usg2MM&)hLnx^1!1LPG&p#9N*S>zsb5>paj#P{fsOQ0&CjY4*~$u8!1W zjH*MFOYfe-5v!Wh1-#uuaTIndhE6|G#GhnQ_p=w{)CfqOV^DD30o7qRk~bo+cyl&f zBd|*NL9~Qtnk!1x>0$FZMRKuXp0-# z4{Zh=VmA&v+8k=sT&k*RJYP5+D7Qc=pbqCR!5Z8DzB!To5T>a^l?C_&ke;dTe0e0U zMfmAFE8B4ke(cTBZ-}Zq8>Ib1ctpF)Qrm2`S-q3lHdmqpCb#%T=teFB(8CRR9#@X* z=oN6le!VO$4dKt>sE0It6W1Yx0S}raGECzS`hCo?XHv{zdWg2qO{_=6fZwe zx>q;ADCBL8Ry%&8u(JNsxyEMF70DS3ZmWjpZgIs$Sm?t6J|jvx9OAYA0B8h0j`VLp zJAcsof0`sHxL;yVg{Z<3hxY)z)+V)qVVjr5lCNl*4?J(X9`nz|_TEmrE+QBsdC0BQ zz*KD}BHhWy1j{y?ex4szy)<99U zWAjR$zo{(8#`Xwq4UH%akd=pbirRbS&VLGP^mQ|C8MOmFGW$c5jvP*nJES`Q@SrPQ ztnu}48m^(Iz*X*M&*%$*FCCaj2pD1k-p3Gmj*si1zhIARiuU-1;uedZF;Z~qvxbw0 zbl-+eASe$FDEG20^SM?H|0&{b!aWSF{!5K9>5G>X!$!XLnRKX_3uqY2Y6I^^Bdqq+ zK071|(d}P4&JCs={U|lTO`PVEK`e*pz=m+NLIT9#Fdzh6O%mzD>w`fw zlbQjbflrdt?|8ge=Gv&ciC!*tBr3p;t<%6B=3)0Tx31l@4z=CU0QIh)wHjTx-1_zu ze*@|1a1Icmr1CI;C~oAH!9+HN6OsT(y&$k1mFE^b*jx(`%;TLnz}Uf6Z%mU8&0FB% z)+vZ-BH(qgj~T0nA)L+ei8ql!XyjmpdURfw0{_x*cH=sz@oBz!9bxLzh$$UdI?k8y zMsfi7y^j&`apB=O@&fOE9@aHBIdznY{4u8UsCIg%6yQ1GX43L@E1aJ~dP@j_B#ufy zGermk5WEk>7DNvJwZq)jIzfX$WS=MdCnL=OqVky!Zw-8jFfCC>^`(R*zNRI^BPX9N z!;ei7g$YQzJ4-{k=Z4@~OL*Ppo@L*qrNc1&1|Ov4TdIm0S}g;8ngo$PK-jKo{AyO0 zkwpsb04J7+fuPej=!!$%^74~3guCxQ2WcGy;y;Sg-Y+@Qtvw~zG`^_P zFKWEWUQ2njmiBfn<6w<_WIfw(J@?Fd{N|6K6yJMV zar2uBozi1^c7Vv!REUou1Cooq<0Dc_dZX5akr2bz5|F}+#KSk8Tx=l@JL?*F#9jGh zq$I+7=Hze~-aVE!H?U5rB_RIT00F-?cnF%%9>CqY8-d&I1Q3ybg+*}X%2`-EbzA1W z=bVY>R33lco*J48a1*ywp!OMUX7(b;ch}74D(5ZlU&lwBoY=-m2^({(sqUW#NV(Yd zx0^Tjv9XO}0+6IPsO5%s73&jG z;e)4y#`9*Rjybmv;%fw3Wy;X`eL&zI21Uve{KC<#4cRc1?)PUjEc&>&Vm_EqDYsAB zDjU*v1J>a5NJI73&$|{K#FH>8DL|Kh(Cq#aZT0zoqzF(wv`vaN%FSC-_@q0)cj>cBYBt z&&~MMxrkSe`#P;BAMKqvl;*?b|F!QROn}CF11G5k+&pa%d~R1(vKQB=*Ub6M@j-dyy79rg1GpZ#B_`UQ8n^SThklm#6od$tTjZ;HOGVL`igQ-Q zI%nWK;04E)Y^ZJAefJN$vQT|5&oLo>q*!g>H zDr<=ijLnd+@i=2^tl<>HOKYyWu9LMHc}%yghAS6#h)jB~|6~m_!QDnYQl@%;rR?G8 zirbvbtk86)l;%ikb|3CkIbVK6uCEQ?mI{IZa_6eK@MqEiO2!uy|MtKO%>=ObZL5oi9rNJDS;Y2 zzx)U&O>tCvqDo5m$!<44#Dj#!r3+6WM2#US=rO}E3u zEYyi*J1tm^r*91V5g^~x2qeedX?z-y_U*)So9Bhc2))+@Bxu?||4o9=NHc+~a?e=r z5@+z3h!R1=HCRhQ1b;8mG7YeG%F#i$MO(JwqLT?4L_X+xS?temLupA09yaPY^!R#P z{Qs&M;6a!c2tvQ{_}o#aN|0**hB&5!Zz$RRAY3jw0px;?t|TP!y7@Q&*|dXGyAMPp zsuwwbtE165El5q0MIsyuEav~I>k*dcrmQ7+RggJlsJ;7~vfM$ZF*+Sj1fjDjL~?c) z5(Md}|Kc!3?$%H$CkHjTOot(Bw96o39M3YMQn~`f|D#o`Z)TN`4a9NaPFLiJ`qL6c z7cj+GMBD2c^p45cU)lo|wnG95k3Y(0yCS$dzDgwy?kwDK-i7Lmuphxk-qi*kv!X9O z8fx99S5uO0VVKt!ROr0(!}nI)Cm-CeE`Fy}o_R-91)?1m%qi>UPLZk_Sd``mF=@v) zuWYVGvGOdD(qk8A6;5#8M#}o~wB!?w($3?lI>ZjB2DjTGn(=}45)N~shdVwVL?{H5 zU!ltYYQLZ5t?sqSzxF3A6v6FKK9ZO&Yw0|G(G;o)1nEV>x6_TSQgFH2_Gp1ZHr!8` zkX$tXAt}eF0MgGnVw2nGnls{s&ucf8iy`~Xco5*`6%Sq*_{(WvQ)aK+?NyJ;wCaX{ z05?PxplNfcD)h;p+!~_)GzNUg8mQz-i!sbGO0A?{b-@ zh0k1|?O!pR9($W_mx`>kr3RV_aylLC?5k0d8ZSUO5xfG4e-y{;ZB!L$GVVG2tgjxD zi?-P_wq%}35P}`DlBS+xlBCc&7&09Q)CXxhQ3qwoz_svTe!l^VO4N;l!$}Y)cK@~f zZ&`fB6=Xa6p)mZ8N+#PuPlmrI*-#!BhMS#OmOSGZIb#~rHdXrBfB0J0qAsb5O}jS>s_i=(TLOo-A(JjxHyTy}#1w;dRF{z-Aa0tv!LuzG&*!7xsDwkb2< zqw=mXQv*c;gilQdW$2oF_eaYqKzil^wZ`(4lG0)WlC@nEl9I^jvW0ADbD$OwRW|Keu~dApcpFC%8O=JQ9bH#|23M1e!GcrSj8=%G z&)r&IQuH8ua$eTQY}yIX6YwL##Vb;~{RYUC-*`oPhg00+$<33?clKDFpz}v9yu3D9Ax_o=(iCXrgO$??_@v(3#rk<Z ze~Oui*5%;>+*@*xWjXMYYyq-l(g^a#IezZO6i%t5h+FO0tnGKiWhm!j!@!tzkfT~m zzz>4iDFIVy4mKCHNWS9BlXTg-Ogv-@&1+s~am0mil&0L=an=<=%MZ}5@@0{z>QpzO&l4`z3f1mU767WQWDP3c==>)3(*9)?Df7hrLSRV?e(uKD#D@}Q zEQTLOv%6#_NIMv7n&tU2yjp$y?wR*V?bv(XEzFR^NGD(2k2k-YW3*kNf`LNc7O>)M zXPNRsx~sf6+XNP9|3LhS&?CiSqpke6i4^DqiO0OB@JeZd>Cg*AVSP+A~gu#&s%HwY`p%F^&Y1o7hqc zz|*31M0`QXeg%gLR-_*5Hp+nE#Cqc;uoK4_`iB8KrOYaPv8Q73aV_K;N{b^2ndbGL z=Ub$-D1cR_IoZhl6m(H`Y*DWgsJ$+(liIGlK>r`^KZ|OjVzLBM3-eNFkqN~U41UD} zkw+3>M%B&Sh3@`&#n;sAJyxS@4^O`Pz1b^9VJozaVs5)K*3%8p z-qP>q=(P1P3`-u91p9?*su6MdoTlpHDP^1@m=)a2XI+`Js9=2Qk%;)wM1UU-q`ML& z02UznCqbP6ydDvWp+yucN`XP=3eq_VT9XJAOGf3Ts@6_=vq!p^HY)SfNoFA&vQIiI z#r7>1w9d_gCik=TTQ0p9vBZJd9bm#K+-zY{QI(X57xRQEHl$x%*!scAijYIQ@)7Qe8Ix6dT^d$&5Q+lgPtXXBkq_0@kBlGqAi|>X!4Clh zkh5Vappu=Fm7>jsYItg7F8E9yMI9LDfC3WzY18DV*?NJSZApuoNy)k|;x%lBx?t1< zNTHC;kRe+EWk~{1Za~E|NiB(1L?gnL&_(wFefsjguvjP-cZ?)>DxMj`of1^cu&hde z5@}CEO#u@hCZ)Wl``ne_Ix(1fno}x#;CWIhVy<y|l^mDrIP@7a<4{fyC$}RBKxo2{b z2NgN*@%Z3qb8aBnhWQ1dDtDxmM=o*ES-#X?y+v zmd3KRY=41Of_A<$zEgQ^Ye_XNNK8 z4rxzfq7pH2lMUBs6t>|c$P*b2v~h+y3YNpj)!2uckVPBfcq#?h3hjV=`}ATVT&W$v zvY|(^c`Vsb`SzKAZ1j-nbDh^C4L%4BpO&!UX7v0-_&h!>P6bE2(K~cGE8%B{{ zsn9~1>_XuTcVZRoUZ={!qJHXStMV2W`xW^3&Ygwo`wP{#b5KP`y}CRYjq>%*Qs(8h z^r6XmomVYpujDkb8>7!#|Ht~-J1pCo4)@7yaZ`9O@TzOZlZPLhDhp;=zw+07EOShM z)-pTp&8vRr*8|~L?6_h~@~hU;Dw$y>0#{$(WHLDOdTiyj798#H0$26iMoN(g9B|0G z6J~Fw-*|qAFq#=CDbWDXEZey|Zx&=dT*!o1-}1c_m^m{!j`DwKx=UK-OWH0=I)O{N zF-v+$OZxds29-<4+Ln$FE}fWN`rqo3;rFGJ$Yql7vXR`fvF@^o`Le0YvRUA=dCam! z(lR-J*|Ku^RNJ!E;Ij4Xvd!wU?f2!=$Q3)`6??fA2i+A%^A#tT73cH5&ID0+pMYb} z5w;YFECIn}F^^GcbPHC1fiP2O9NfZL63zqXRM3jwU0ilpgssU7^2bf`v5l?_HOK>ERu;wtwwDF>CY(% zysHdb3hz9?GX!w=fZPfY$Er2u=S+k7wSuH|UNR+9E|_6X36J^cBh1Ka^K*@(bI-4( z@oq#Ntd+~H-_{L@HC*=>PKaL(R!HLZz%lrUlwjVF1Y1gZWiUf-9k;`{8~DMEok-L6 zflA?^gr2nom!S07ko&?R(a+a2ZwB+=5)4J*7K;o!g`mbGAG{hF^!oMKYr*%=Z`N$D z_1+Aw&)6)U1$pKHUN^eu>L%ei!fYPwjR|dAU29Vd;UjMK*oGX$#eC#xWIU_f`k!cU zjT$9GIAr$Br?NBQ!M0ns54O1HL7D}JGL`f`KPjm2dDz^|%_oB!C97K#F7P)8zMf#> z{iIKQfuDMHw@Q%fIc@8;s~e@}+ee-wR2ny4B%LpM9`Yb&vx9dl(J*9paC_^yug4-o zhXCOo-+C~+@pF4^UTtGKKYVXBB-kZk-1&m|bA-bh>@WbiAs1YJX6x;>&HJ><^~}oc z1Y4@FZKBQ4r+>Wfy+q+@$c-683Y+)BOX1JIbVD+Aci&smPN)TQXq6GT&j7ytqdS}F*a*WDE@WSWIJHL#2Qm-OQ}`BlkIj8K zTyUOaPR%`lGuIe+(ynaPj^5m&DQbHx98KAT!>rJYaco@IKt!Bq>wiw@`>`Ygwt)hoWxaXA{@dM!!_$rK@f6%Pv2 z6U{tL`B-inWDxfa&)P~{gZId-;~RH9)!%zj7(#^4965+Az%X}X=8l|Y`YfZkk2>STqaZcb970}<3cBv&@m~rZ;69}tfja7;oP82r@CF{&XSo3kyKy36 zo|=6C(Di621BGA#RToU=A4zcEVY%a==L-UHq&V+J7|#yG zSt0g?%Vy$9lmYmeZ;g@Y24~3!^Ri)g>*ILMnZjU1+bobZM|=-5($0Q#dx4OO3!=S< z#p6QwSn-Jp@kK6|y!8>4L0h+z5=%ZWx^iGN-tqm0Dulrf-CUpLF&`6s29lwUC7@O% z_fxzGaZi%qMFp#J`Y1Odo%j^wsfwz5x9By*ILzM6kc&wK8By0DuIj0hz)xQC?<){! zxEv%`52Xiw;RfLTFZKz*?*k#=XQm&p|`j{pKydP5Vu3e6Cl;P@dJ97 z<8J;*B*VhDK{HZJz*9gn?)Ont;8EhQ_J35 zC8b;v_KayGSdzBj>8x?~`+Hnu!Q)qZ0RQrOcUkR2(FfJY(6L6pr`f8Z-&Gb`L*|;p zYh0#NxVesx3vJL9dL2Cv4-f1ll~-4a76Wo$zApNtbi8J}nQBaP)$>__k)b~|S4WHW zE^crAyy)g`RYIE;y3~lC*U3H_*qEX-hvOEVwL4_?BvFDKLuQNKaT9VgxPD!}4Dm~U zcX_x#>%z9zMKz;Cyyx{7jMU!Y@1`VB$v=q6pWq_VJwO!&`2-FL7KOHbP!#ZLI{uBg zW^nN5*N?r=pZ`TFMyy8(UKRDH9x-a#2fe+IhUL)@r*zQD;3}jdE>VUODX9fgsJUf0n~BOHd`>3Z4$*Iyh)C7o^el#akBaDAsw;>ax2-XGRO4;Jh)^SyKZQiE@Hp9iD`gji0Dj& z9s0&{Ic*px6}SkwU^yvrJ5A4jQnrjKeo3MH=Ze&bj0+Vk_40PNWGj_b`Q6T}u$vw- z3LSE=SB)`Uo#s+zQ%kci4Hw>alpxTPBa(-YDc9X!5EoW0rtm}QyxsTs{eSSwlz6F4 zPmrY}fiG-2*|%pQdScQd0OeV``?l_7$V1f!wAa^)p7EdCSS~cD=dp(r{`-97hOv^{ zr&?ezq`;ex_@6ZFxjZ*Lw}8!M@u&GO)CuoYw5`8_q*?9zG>U_W{I zYy~ZT+#|H6>2-4-@$_%Iu|CE1ab)t>6FM`_o`)UOE;=2$SY2aw?DJ&hjLP?0nrFG5 z&IH$V$GoUtZj71~Y*CV^FfjJ==ofJH`aX24`LEt0J#_Q?Y(=uCc=c&6)$bpS8n`cd zOXhR#E#`S%`~E9j~+VRTNbTPLRK^N~NlRi7S$$~NTQGW_@VH=lsg`5l$6XwtPum*0JE zuT-Kb)o}#>T_(V#S$LP8q2p;&QxGk;&ThztqM9fRZldsn{|rOOpm7nQA*#DMRzuwx z>A`wgJQ17Rp*IkXd)5k$2}Ndx`b-HRBT0|M@lC83IcA4Es8D_e0H4yZ;aXWShR@nC z%~^T;EmdYCD+xk*?0#u(Bc$3IZ;Edy?e*F|G(q~1B-VW#DyGu(Saq@~!|8Xn>h2oG z#?6;mli)h(B`STyA1YLvPjf=9mGNewSIiaq@v;NzyFHTwzjw){qtXL3?qiC=H>UR5Y} zGrGQBLEtOj&sLVpLcF09_`Wjy704zbRbI7FUiU)Afp9j~NwA!YO1?)vr{t&-zISHc zBlq}KgQ?QpX@g(=L#Hn*O_$;C6RXVsr{s`RJ$*;>{yp2PN=~ Date: Sun, 31 Jul 2016 23:03:14 +0200 Subject: [PATCH 02/46] switch off assertions for benchmarks --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 15d00f6e..70e8859d 100644 --- a/Makefile +++ b/Makefile @@ -87,7 +87,7 @@ pretty: # benchmarks json_benchmarks: benchmarks/benchmarks.cpp benchmarks/benchpress.hpp benchmarks/cxxopts.hpp src/json.hpp cd benchmarks/files/numbers ; python generate.py - $(CXX) -std=c++11 $(CXXFLAGS) -O3 -flto -I src -I benchmarks $< $(LDFLAGS) -o $@ + $(CXX) -std=c++11 $(CXXFLAGS) -DNDEBUG -O3 -flto -I src -I benchmarks $< $(LDFLAGS) -o $@ ./json_benchmarks From 5541e6f6f90162ae42c6935335aa320d87b42b76 Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 07:24:46 +0200 Subject: [PATCH 03/46] split unit tests --- .gitignore | 2 ++ .travis.yml | 6 +++--- Makefile | 12 +++++++----- README.md | 3 +-- test/CMakeLists.txt | 1 + test/Makefile | 21 +++++++++++++++++++++ test/src/unit-runner.cpp | 30 ++++++++++++++++++++++++++++++ test/src/unit.cpp | 2 +- 8 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 test/Makefile create mode 100644 test/src/unit-runner.cpp diff --git a/.gitignore b/.gitignore index fd41a2e3..b82214e9 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ android doc/xml benchmarks/files/numbers/*.json + +*.o diff --git a/.travis.yml b/.travis.yml index ffe05ec6..4af6bf74 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ matrix: - make clean - touch src/json.hpp - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - - ./json_unit "*" + - test/json_unit "*" - coveralls --exclude test/src/catch.hpp --exclude test/src/unit.cpp --include src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' - bash <(curl -s https://codecov.io/bash) env: COMPILER=g++-4.9 @@ -164,9 +164,9 @@ script: - uname -a - $COMPILER --version - make CXX=$COMPILER - - ./json_unit "*" + - test/json_unit "*" - if [ `which valgrind` ]; then - valgrind --error-exitcode=1 --leak-check=full ./json_unit ; + valgrind --error-exitcode=1 --leak-check=full test/json_unit ; fi - if [ `which brew` ]; then brew update ; diff --git a/Makefile b/Makefile index 70e8859d..199d65da 100644 --- a/Makefile +++ b/Makefile @@ -12,18 +12,20 @@ clean: rm -fr json_unit json_benchmarks fuzz fuzz-testing *.dSYM rm -fr benchmarks/files/numbers/*.json $(MAKE) clean -Cdoc + $(MAKE) clean -Ctest ########################################################################## # unit tests ########################################################################## -# additional flags -FLAGS = -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wfloat-equal +# build unit tests +json_unit: + @$(MAKE) -C test -# build unit tests (TODO: Does this want its own makefile?) -json_unit: test/src/unit.cpp src/json.hpp test/src/catch.hpp - $(CXX) -std=c++11 $(CXXFLAGS) $(FLAGS) $(CPPFLAGS) -I src -I test $< $(LDFLAGS) -o $@ +# run unit tests +check: json_unit + test/json_unit "*" ########################################################################## diff --git a/README.md b/README.md index c0bb61b1..722bf880 100644 --- a/README.md +++ b/README.md @@ -501,8 +501,7 @@ Thanks a lot for helping out! To compile and run the tests, you need to execute ```sh -$ make -$ ./json_unit "*" +$ make check =============================================================================== All tests passed (8905012 assertions in 32 test cases) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c66b19c8..4d12de37 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,6 +3,7 @@ set(JSON_UNITTEST_TARGET_NAME "json_unit") add_executable(${JSON_UNITTEST_TARGET_NAME} "src/catch.hpp" "src/unit.cpp" + "src/unit-runner.cpp" ) set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 00000000..0d4edec7 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,21 @@ +########################################################################## +# unit tests +########################################################################## + +# additional flags +CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wfloat-equal +INCDIRS = -I ../src -I . + +SOURCES = src/unit-runner.cpp src/unit.cpp +OBJECTS = $(SOURCES:.cpp=.o) + +all: json_unit + +json_unit: $(OBJECTS) ../src/json.hpp src/catch.hpp + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) -o $@ + +%.o: %.cpp + $(CXX) $(CXXFLAGS) $(INCDIRS) -c $< -o $@ + +clean: + rm -fr json_unit $(OBJECTS) diff --git a/test/src/unit-runner.cpp b/test/src/unit-runner.cpp new file mode 100644 index 00000000..ec957b7a --- /dev/null +++ b/test/src/unit-runner.cpp @@ -0,0 +1,30 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#define CATCH_CONFIG_MAIN +#include "catch.hpp" diff --git a/test/src/unit.cpp b/test/src/unit.cpp index 79a4bb09..a5c348bb 100644 --- a/test/src/unit.cpp +++ b/test/src/unit.cpp @@ -26,12 +26,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#define CATCH_CONFIG_MAIN #include "catch.hpp" #include #include #include +#include #include #include #include From be5cf0e3bacbcc1282132df077fef1870711f799 Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 07:33:44 +0200 Subject: [PATCH 04/46] forgot to pass CPPFLAGS --- test/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Makefile b/test/Makefile index 0d4edec7..d11e1e34 100644 --- a/test/Makefile +++ b/test/Makefile @@ -4,7 +4,7 @@ # additional flags CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wfloat-equal -INCDIRS = -I ../src -I . +CPPFLAGS += -I ../src -I . SOURCES = src/unit-runner.cpp src/unit.cpp OBJECTS = $(SOURCES:.cpp=.o) @@ -15,7 +15,7 @@ json_unit: $(OBJECTS) ../src/json.hpp src/catch.hpp $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) -o $@ %.o: %.cpp - $(CXX) $(CXXFLAGS) $(INCDIRS) -c $< -o $@ + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ clean: rm -fr json_unit $(OBJECTS) From 3944ecd470f50d99bb7020b330a3caf6fbc34d2a Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 07:40:04 +0200 Subject: [PATCH 05/46] chose correct flags --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4af6bf74..06f25879 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,7 +61,7 @@ matrix: - LLVM_ARCHIVE_PATH=$HOME/clang+llvm.tar.xz - COMPILER=clang++ - CPPFLAGS="-I $HOME/clang-$LLVM_VERSION/include/c++/v1" - - CXXFLAGS=-lc++ + - LDFLAGS=-lc++ - PATH=$HOME/clang-$LLVM_VERSION/bin:$PATH - LD_LIBRARY_PATH=$HOME/clang-$LLVM_VERSION/lib:$LD_LIBRARY_PATH before_install: From d80329034e568b8f52ced7aa537581757b75b426 Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 21:55:47 +0200 Subject: [PATCH 06/46] split test suite in one file per test case --- Makefile | 3 +- test/CMakeLists.txt | 28 +- test/Makefile | 40 +- test/src/unit-algorithms.cpp | 318 + .../{unit-runner.cpp => unit-allocator.cpp} | 35 +- test/src/unit-capacity.cpp | 562 + test/src/unit-class_const_iterator.cpp | 401 + test/src/unit-class_iterator.cpp | 401 + test/src/unit-class_lexer.cpp | 155 + test/src/unit-class_parser.cpp | 753 + test/src/unit-comparison.cpp | 246 + test/src/unit-concepts.cpp | 169 + test/src/unit-constructor.cpp | 1470 ++ test/src/unit-convenience.cpp | 92 + test/src/unit-conversions.cpp | 1014 ++ test/src/unit-deserialization.cpp | 73 + test/src/unit-element_access.cpp | 1871 ++ test/src/unit-inspection.cpp | 373 + test/src/unit-iterator_wrapper.cpp | 729 + test/src/unit-iterators.cpp | 2352 +++ test/src/unit-json_patch.cpp | 1217 ++ test/src/unit-json_pointer.cpp | 388 + test/src/unit-modifiers.cpp | 713 + test/src/unit-pointer_access.cpp | 263 + test/src/unit-readme.cpp | 299 + test/src/unit-reference_access.cpp | 202 + test/src/unit-regression.cpp | 443 + test/src/unit-serialization.cpp | 76 + test/src/unit-testsuites.cpp | 436 + test/src/unit-unicode.cpp | 176 + test/src/unit.cpp | 14379 +--------------- 31 files changed, 15291 insertions(+), 14386 deletions(-) create mode 100644 test/src/unit-algorithms.cpp rename test/src/{unit-runner.cpp => unit-allocator.cpp} (64%) create mode 100644 test/src/unit-capacity.cpp create mode 100644 test/src/unit-class_const_iterator.cpp create mode 100644 test/src/unit-class_iterator.cpp create mode 100644 test/src/unit-class_lexer.cpp create mode 100644 test/src/unit-class_parser.cpp create mode 100644 test/src/unit-comparison.cpp create mode 100644 test/src/unit-concepts.cpp create mode 100644 test/src/unit-constructor.cpp create mode 100644 test/src/unit-convenience.cpp create mode 100644 test/src/unit-conversions.cpp create mode 100644 test/src/unit-deserialization.cpp create mode 100644 test/src/unit-element_access.cpp create mode 100644 test/src/unit-inspection.cpp create mode 100644 test/src/unit-iterator_wrapper.cpp create mode 100644 test/src/unit-iterators.cpp create mode 100644 test/src/unit-json_patch.cpp create mode 100644 test/src/unit-json_pointer.cpp create mode 100644 test/src/unit-modifiers.cpp create mode 100644 test/src/unit-pointer_access.cpp create mode 100644 test/src/unit-readme.cpp create mode 100644 test/src/unit-reference_access.cpp create mode 100644 test/src/unit-regression.cpp create mode 100644 test/src/unit-serialization.cpp create mode 100644 test/src/unit-testsuites.cpp create mode 100644 test/src/unit-unicode.cpp diff --git a/Makefile b/Makefile index 199d65da..b6116cc4 100644 --- a/Makefile +++ b/Makefile @@ -79,7 +79,8 @@ pretty: --indent-col1-comments --pad-oper --pad-header --align-pointer=type \ --align-reference=type --add-brackets --convert-tabs --close-templates \ --lineend=linux --preserve-date --suffix=none --formatted \ - src/json.hpp src/json.hpp.re2c test/src/unit.cpp test/src/fuzz.cpp benchmarks/benchmarks.cpp doc/examples/*.cpp + src/json.hpp src/json.hpp.re2c test/src/*.cpp \ + benchmarks/benchmarks.cpp doc/examples/*.cpp ########################################################################## diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4d12de37..696a7f3f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,7 +3,33 @@ set(JSON_UNITTEST_TARGET_NAME "json_unit") add_executable(${JSON_UNITTEST_TARGET_NAME} "src/catch.hpp" "src/unit.cpp" - "src/unit-runner.cpp" + "src/unit-algorithms.cpp" + "src/unit-allocator.cpp" + "src/unit-capacity.cpp" + "src/unit-class_const_iterator.cpp" + "src/unit-class_iterator.cpp" + "src/unit-class_lexer.cpp" + "src/unit-class_parser.cpp" + "src/unit-comparison.cpp" + "src/unit-concepts.cpp" + "src/unit-constructor.cpp" + "src/unit-convenience.cpp" + "src/unit-conversions.cpp" + "src/unit-deserialization.cpp" + "src/unit-element_access.cpp" + "src/unit-inspection.cpp" + "src/unit-iterator_wrapper.cpp" + "src/unit-iterators.cpp" + "src/unit-json_patch.cpp" + "src/unit-json_pointer.cpp" + "src/unit-modifiers.cpp" + "src/unit-pointer_access.cpp" + "src/unit-readme.cpp" + "src/unit-reference_access.cpp" + "src/unit-regression.cpp" + "src/unit-serialization.cpp" + "src/unit-testsuites.cpp" + "src/unit-unicode.cpp" ) set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES diff --git a/test/Makefile b/test/Makefile index d11e1e34..c56a2ae9 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,19 +3,49 @@ ########################################################################## # additional flags -CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wfloat-equal +CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wno-keyword-macro -Wno-float-equal CPPFLAGS += -I ../src -I . -SOURCES = src/unit-runner.cpp src/unit.cpp +SOURCES = src/unit.cpp \ + src/unit-algorithms.cpp \ + src/unit-allocator.cpp \ + src/unit-capacity.cpp \ + src/unit-class_const_iterator.cpp \ + src/unit-class_iterator.cpp \ + src/unit-class_lexer.cpp \ + src/unit-class_parser.cpp \ + src/unit-comparison.cpp \ + src/unit-concepts.cpp \ + src/unit-constructor.cpp \ + src/unit-convenience.cpp \ + src/unit-conversions.cpp \ + src/unit-deserialization.cpp \ + src/unit-element_access.cpp \ + src/unit-inspection.cpp \ + src/unit-iterator_wrapper.cpp \ + src/unit-iterators.cpp \ + src/unit-json_patch.cpp \ + src/unit-json_pointer.cpp \ + src/unit-modifiers.cpp \ + src/unit-pointer_access.cpp \ + src/unit-readme.cpp \ + src/unit-reference_access.cpp \ + src/unit-regression.cpp \ + src/unit-serialization.cpp \ + src/unit-unicode.cpp \ + src/unit-testsuites.cpp + OBJECTS = $(SOURCES:.cpp=.o) all: json_unit json_unit: $(OBJECTS) ../src/json.hpp src/catch.hpp - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) -o $@ + @echo "[CXXLD] $@" + @$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) -o $@ -%.o: %.cpp - $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ +%.o: %.cpp ../src/json.hpp src/catch.hpp + @echo "[CXX] $@" + @$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ clean: rm -fr json_unit $(OBJECTS) diff --git a/test/src/unit-algorithms.cpp b/test/src/unit-algorithms.cpp new file mode 100644 index 00000000..75c69da5 --- /dev/null +++ b/test/src/unit-algorithms.cpp @@ -0,0 +1,318 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("algorithms") +{ + json j_array = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz"}; + json j_object = {{"one", 1}, {"two", 2}}; + + SECTION("non-modifying sequence operations") + { + SECTION("std::all_of") + { + CHECK(std::all_of(j_array.begin(), j_array.end(), [](const json & value) + { + return value.size() > 0; + })); + CHECK(std::all_of(j_object.begin(), j_object.end(), [](const json & value) + { + return value.type() == json::value_t::number_integer; + })); + } + + SECTION("std::any_of") + { + CHECK(std::any_of(j_array.begin(), j_array.end(), [](const json & value) + { + return value.is_string() and value.get() == "foo"; + })); + CHECK(std::any_of(j_object.begin(), j_object.end(), [](const json & value) + { + return value.get() > 1; + })); + } + + SECTION("std::none_of") + { + CHECK(std::none_of(j_array.begin(), j_array.end(), [](const json & value) + { + return value.size() == 0; + })); + CHECK(std::none_of(j_object.begin(), j_object.end(), [](const json & value) + { + return value.get() <= 0; + })); + } + + SECTION("std::for_each") + { + SECTION("reading") + { + int sum = 0; + + std::for_each(j_array.cbegin(), j_array.cend(), [&sum](const json & value) + { + if (value.is_number()) + { + sum += static_cast(value); + } + }); + + CHECK(sum == 45); + } + + SECTION("writing") + { + auto add17 = [](json & value) + { + if (value.is_array()) + { + value.push_back(17); + } + }; + + std::for_each(j_array.begin(), j_array.end(), add17); + + CHECK(j_array[6] == json({1, 2, 3, 17})); + } + } + + SECTION("std::count") + { + CHECK(std::count(j_array.begin(), j_array.end(), json(true)) == 1); + } + + SECTION("std::count_if") + { + CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json & value) + { + return (value.is_number()); + }) == 3); + CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json&) + { + return true; + }) == 9); + } + + SECTION("std::mismatch") + { + json j_array2 = {13, 29, 3, {{"one", 1}, {"two", 2}, {"three", 3}}, true, false, {1, 2, 3}, "foo", "baz"}; + auto res = std::mismatch(j_array.begin(), j_array.end(), j_array2.begin()); + CHECK(*res.first == json({{"one", 1}, {"two", 2}})); + CHECK(*res.second == json({{"one", 1}, {"two", 2}, {"three", 3}})); + } + + SECTION("std::equal") + { + SECTION("using operator==") + { + CHECK(std::equal(j_array.begin(), j_array.end(), j_array.begin())); + CHECK(std::equal(j_object.begin(), j_object.end(), j_object.begin())); + CHECK(not std::equal(j_array.begin(), j_array.end(), j_object.begin())); + } + + SECTION("using user-defined comparison") + { + // compare objects only by size of its elements + json j_array2 = {13, 29, 3, {"Hello", "World"}, true, false, {{"one", 1}, {"two", 2}, {"three", 3}}, "foo", "baz"}; + CHECK(not std::equal(j_array.begin(), j_array.end(), j_array2.begin())); + CHECK(std::equal(j_array.begin(), j_array.end(), j_array2.begin(), + [](const json & a, const json & b) + { + return (a.size() == b.size()); + })); + } + } + + SECTION("std::find") + { + auto it = std::find(j_array.begin(), j_array.end(), json(false)); + CHECK(std::distance(j_array.begin(), it) == 5); + } + + SECTION("std::find_if") + { + auto it = std::find_if(j_array.begin(), j_array.end(), + [](const json & value) + { + return value.is_boolean(); + }); + CHECK(std::distance(j_array.begin(), it) == 4); + } + + SECTION("std::find_if_not") + { + auto it = std::find_if_not(j_array.begin(), j_array.end(), + [](const json & value) + { + return value.is_number(); + }); + CHECK(std::distance(j_array.begin(), it) == 3); + } + + SECTION("std::adjacent_find") + { + CHECK(std::adjacent_find(j_array.begin(), j_array.end()) == j_array.end()); + CHECK(std::adjacent_find(j_array.begin(), j_array.end(), + [](const json & v1, const json & v2) + { + return v1.type() == v2.type(); + }) == j_array.begin()); + } + } + + SECTION("modifying sequence operations") + { + SECTION("std::reverse") + { + std::reverse(j_array.begin(), j_array.end()); + CHECK(j_array == json({"baz", "foo", {1, 2, 3}, false, true, {{"one", 1}, {"two", 2}}, 3, 29, 13})); + } + + SECTION("std::rotate") + { + std::rotate(j_array.begin(), j_array.begin() + 1, j_array.end()); + CHECK(j_array == json({29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", 13})); + } + + SECTION("std::partition") + { + auto it = std::partition(j_array.begin(), j_array.end(), [](const json & v) + { + return v.is_string(); + }); + CHECK(std::distance(j_array.begin(), it) == 2); + CHECK(not it[2].is_string()); + } + } + + SECTION("sorting operations") + { + SECTION("std::sort") + { + SECTION("with standard comparison") + { + json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr}; + std::sort(j.begin(), j.end()); + CHECK(j == json({nullptr, false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"})); + } + + SECTION("with user-defined comparison") + { + json j = {3, {{"one", 1}, {"two", 2}}, {1, 2, 3}, nullptr}; + std::sort(j.begin(), j.end(), [](const json & a, const json & b) + { + return a.size() < b.size(); + }); + CHECK(j == json({nullptr, 3, {{"one", 1}, {"two", 2}}, {1, 2, 3}})); + } + + SECTION("sorting an object") + { + json j({{"one", 1}, {"two", 2}}); + CHECK_THROWS_AS(std::sort(j.begin(), j.end()), std::domain_error); + CHECK_THROWS_WITH(std::sort(j.begin(), j.end()), "cannot use offsets with object iterators"); + } + } + + SECTION("std::partial_sort") + { + json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr}; + std::partial_sort(j.begin(), j.begin() + 4, j.end()); + CHECK(j == json({nullptr, false, true, 3, {{"one", 1}, {"two", 2}}, 29, {1, 2, 3}, "foo", "baz", 13})); + } + } + + SECTION("set operations") + { + SECTION("std::merge") + { + { + json j1 = {2, 4, 6, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::merge(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({1, 2, 2, 3, 4, 5, 6, 7, 8})); + } + } + + SECTION("std::set_difference") + { + json j1 = {1, 2, 3, 4, 5, 6, 7, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::set_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({4, 6, 8})); + } + + SECTION("std::set_intersection") + { + json j1 = {1, 2, 3, 4, 5, 6, 7, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::set_intersection(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({1, 2, 3, 5, 7})); + } + + SECTION("std::set_union") + { + json j1 = {2, 4, 6, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::set_union(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({1, 2, 3, 4, 5, 6, 7, 8})); + } + + SECTION("std::set_symmetric_difference") + { + json j1 = {2, 4, 6, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::set_symmetric_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({1, 3, 4, 5, 6, 7, 8})); + } + } + + SECTION("heap operations") + { + std::make_heap(j_array.begin(), j_array.end()); + CHECK(std::is_heap(j_array.begin(), j_array.end())); + std::sort_heap(j_array.begin(), j_array.end()); + CHECK(j_array == json({false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"})); + } +} diff --git a/test/src/unit-runner.cpp b/test/src/unit-allocator.cpp similarity index 64% rename from test/src/unit-runner.cpp rename to test/src/unit-allocator.cpp index ec957b7a..dcf8aa35 100644 --- a/test/src/unit-runner.cpp +++ b/test/src/unit-allocator.cpp @@ -26,5 +26,38 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#define CATCH_CONFIG_MAIN #include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +// special test case to check if memory is leaked if constructor throws + +template +struct my_allocator : std::allocator +{ + template + void construct(T*, Args&& ...) + { + throw std::bad_alloc(); + } +}; + +TEST_CASE("bad_alloc") +{ + SECTION("bad_alloc") + { + // create JSON type using the throwing allocator + using my_json = nlohmann::basic_json; + + // creating an object should throw + CHECK_THROWS_AS(my_json j(my_json::value_t::object), std::bad_alloc); + } +} diff --git a/test/src/unit-capacity.cpp b/test/src/unit-capacity.cpp new file mode 100644 index 00000000..95100da9 --- /dev/null +++ b/test/src/unit-capacity.cpp @@ -0,0 +1,562 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("capacity") +{ + SECTION("empty()") + { + SECTION("boolean") + { + json j = true; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + + SECTION("string") + { + json j = "hello world"; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + + SECTION("array") + { + SECTION("empty array") + { + json j = json::array(); + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == true); + CHECK(j_const.empty() == true); + } + + SECTION("definition of empty") + { + CHECK(j.begin() == j.end()); + CHECK(j_const.begin() == j_const.end()); + } + } + + SECTION("filled array") + { + json j = {1, 2, 3}; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + } + + SECTION("object") + { + SECTION("empty object") + { + json j = json::object(); + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == true); + CHECK(j_const.empty() == true); + } + + SECTION("definition of empty") + { + CHECK(j.begin() == j.end()); + CHECK(j_const.begin() == j_const.end()); + } + } + + SECTION("filled object") + { + json j = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + } + + SECTION("number (integer)") + { + json j = 23; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + + SECTION("number (unsigned)") + { + json j = 23u; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + + SECTION("number (float)") + { + json j = 23.42; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + + SECTION("null") + { + json j = nullptr; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == true); + CHECK(j_const.empty() == true); + } + + SECTION("definition of empty") + { + CHECK(j.begin() == j.end()); + CHECK(j_const.begin() == j_const.end()); + } + } + } + + SECTION("size()") + { + SECTION("boolean") + { + json j = true; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 1); + CHECK(j_const.size() == 1); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + + SECTION("string") + { + json j = "hello world"; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 1); + CHECK(j_const.size() == 1); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + + SECTION("array") + { + SECTION("empty array") + { + json j = json::array(); + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 0); + CHECK(j_const.size() == 0); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + + SECTION("filled array") + { + json j = {1, 2, 3}; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 3); + CHECK(j_const.size() == 3); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + } + + SECTION("object") + { + SECTION("empty object") + { + json j = json::object(); + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 0); + CHECK(j_const.size() == 0); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + + SECTION("filled object") + { + json j = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 3); + CHECK(j_const.size() == 3); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + } + + SECTION("number (integer)") + { + json j = 23; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 1); + CHECK(j_const.size() == 1); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + + SECTION("number (unsigned)") + { + json j = 23u; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 1); + CHECK(j_const.size() == 1); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + + SECTION("number (float)") + { + json j = 23.42; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 1); + CHECK(j_const.size() == 1); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + + SECTION("null") + { + json j = nullptr; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 0); + CHECK(j_const.size() == 0); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + } + + SECTION("max_size()") + { + SECTION("boolean") + { + json j = true; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() == 1); + CHECK(j_const.max_size() == 1); + } + } + + SECTION("string") + { + json j = "hello world"; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() == 1); + CHECK(j_const.max_size() == 1); + } + } + + SECTION("array") + { + SECTION("empty array") + { + json j = json::array(); + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() >= j.size()); + CHECK(j_const.max_size() >= j_const.size()); + } + } + + SECTION("filled array") + { + json j = {1, 2, 3}; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() >= j.size()); + CHECK(j_const.max_size() >= j_const.size()); + } + } + } + + SECTION("object") + { + SECTION("empty object") + { + json j = json::object(); + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() >= j.size()); + CHECK(j_const.max_size() >= j_const.size()); + } + } + + SECTION("filled object") + { + json j = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() >= j.size()); + CHECK(j_const.max_size() >= j_const.size()); + } + } + } + + SECTION("number (integer)") + { + json j = 23; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() == 1); + CHECK(j_const.max_size() == 1); + } + } + + SECTION("number (unsigned)") + { + json j = 23u; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() == 1); + CHECK(j_const.max_size() == 1); + } + } + + SECTION("number (float)") + { + json j = 23.42; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() == 1); + CHECK(j_const.max_size() == 1); + } + } + + SECTION("null") + { + json j = nullptr; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() == 0); + CHECK(j_const.max_size() == 0); + } + } + } +} diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp new file mode 100644 index 00000000..4908b5ce --- /dev/null +++ b/test/src/unit-class_const_iterator.cpp @@ -0,0 +1,401 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("const_iterator class") +{ + SECTION("construction") + { + SECTION("constructor") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it(&j); + } + + SECTION("object") + { + json j(json::value_t::object); + json::const_iterator it(&j); + } + + SECTION("array") + { + json j(json::value_t::array); + json::const_iterator it(&j); + } + } + + SECTION("copy assignment") + { + json j(json::value_t::null); + json::const_iterator it(&j); + json::const_iterator it2(&j); + it2 = it; + } + } + + SECTION("initialization") + { + SECTION("set_begin") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it(&j); + it.set_begin(); + CHECK(it == j.cbegin()); + } + + SECTION("object") + { + json j(json::value_t::object); + json::const_iterator it(&j); + it.set_begin(); + CHECK(it == j.cbegin()); + } + + SECTION("array") + { + json j(json::value_t::array); + json::const_iterator it(&j); + it.set_begin(); + CHECK(it == j.cbegin()); + } + } + + SECTION("set_end") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it(&j); + it.set_end(); + CHECK(it == j.cend()); + } + + SECTION("object") + { + json j(json::value_t::object); + json::const_iterator it(&j); + it.set_end(); + CHECK(it == j.cend()); + } + + SECTION("array") + { + json j(json::value_t::array); + json::const_iterator it(&j); + it.set_end(); + CHECK(it == j.cend()); + } + } + } + + SECTION("element access") + { + SECTION("operator*") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it = j.cbegin(); + CHECK_THROWS_AS(*it, std::out_of_range); + CHECK_THROWS_WITH(*it, "cannot get value"); + } + + SECTION("number") + { + json j(17); + json::const_iterator it = j.cbegin(); + CHECK(*it == json(17)); + it = j.cend(); + CHECK_THROWS_AS(*it, std::out_of_range); + CHECK_THROWS_WITH(*it, "cannot get value"); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::const_iterator it = j.cbegin(); + CHECK(*it == json("bar")); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::const_iterator it = j.cbegin(); + CHECK(*it == json(1)); + } + } + + SECTION("operator->") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it = j.cbegin(); + CHECK_THROWS_AS(it->type_name(), std::out_of_range); + CHECK_THROWS_WITH(it->type_name(), "cannot get value"); + } + + SECTION("number") + { + json j(17); + json::const_iterator it = j.cbegin(); + CHECK(it->type_name() == "number"); + it = j.cend(); + CHECK_THROWS_AS(it->type_name(), std::out_of_range); + CHECK_THROWS_WITH(it->type_name(), "cannot get value"); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::const_iterator it = j.cbegin(); + CHECK(it->type_name() == "string"); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::const_iterator it = j.cbegin(); + CHECK(it->type_name() == "number"); + } + } + } + + SECTION("increment/decrement") + { + SECTION("post-increment") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.primitive_iterator == 1); + it++; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("number") + { + json j(17); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.primitive_iterator == 0); + it++; + CHECK(it.m_it.primitive_iterator == 1); + it++; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + it++; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + } + } + + SECTION("pre-increment") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.primitive_iterator == 1); + ++it; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("number") + { + json j(17); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.primitive_iterator == 0); + ++it; + CHECK(it.m_it.primitive_iterator == 1); + ++it; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + ++it; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + } + } + + SECTION("post-decrement") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it = j.cend(); + CHECK(it.m_it.primitive_iterator == 1); + } + + SECTION("number") + { + json j(17); + json::const_iterator it = j.cend(); + CHECK(it.m_it.primitive_iterator == 1); + it--; + CHECK(it.m_it.primitive_iterator == 0); + it--; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::const_iterator it = j.cend(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + it--; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::const_iterator it = j.cend(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + } + } + + SECTION("pre-decrement") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it = j.cend(); + CHECK(it.m_it.primitive_iterator == 1); + } + + SECTION("number") + { + json j(17); + json::const_iterator it = j.cend(); + CHECK(it.m_it.primitive_iterator == 1); + --it; + CHECK(it.m_it.primitive_iterator == 0); + --it; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::const_iterator it = j.cend(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + --it; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::const_iterator it = j.cend(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + } + } + } +} diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp new file mode 100644 index 00000000..1d4b2901 --- /dev/null +++ b/test/src/unit-class_iterator.cpp @@ -0,0 +1,401 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("iterator class") +{ + SECTION("construction") + { + SECTION("constructor") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it(&j); + } + + SECTION("object") + { + json j(json::value_t::object); + json::iterator it(&j); + } + + SECTION("array") + { + json j(json::value_t::array); + json::iterator it(&j); + } + } + + SECTION("copy assignment") + { + json j(json::value_t::null); + json::iterator it(&j); + json::iterator it2(&j); + it2 = it; + } + } + + SECTION("initialization") + { + SECTION("set_begin") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it(&j); + it.set_begin(); + CHECK(it == j.begin()); + } + + SECTION("object") + { + json j(json::value_t::object); + json::iterator it(&j); + it.set_begin(); + CHECK(it == j.begin()); + } + + SECTION("array") + { + json j(json::value_t::array); + json::iterator it(&j); + it.set_begin(); + CHECK(it == j.begin()); + } + } + + SECTION("set_end") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it(&j); + it.set_end(); + CHECK(it == j.end()); + } + + SECTION("object") + { + json j(json::value_t::object); + json::iterator it(&j); + it.set_end(); + CHECK(it == j.end()); + } + + SECTION("array") + { + json j(json::value_t::array); + json::iterator it(&j); + it.set_end(); + CHECK(it == j.end()); + } + } + } + + SECTION("element access") + { + SECTION("operator*") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it = j.begin(); + CHECK_THROWS_AS(*it, std::out_of_range); + CHECK_THROWS_WITH(*it, "cannot get value"); + } + + SECTION("number") + { + json j(17); + json::iterator it = j.begin(); + CHECK(*it == json(17)); + it = j.end(); + CHECK_THROWS_AS(*it, std::out_of_range); + CHECK_THROWS_WITH(*it, "cannot get value"); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::iterator it = j.begin(); + CHECK(*it == json("bar")); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::iterator it = j.begin(); + CHECK(*it == json(1)); + } + } + + SECTION("operator->") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it = j.begin(); + CHECK_THROWS_AS(it->type_name(), std::out_of_range); + CHECK_THROWS_WITH(it->type_name(), "cannot get value"); + } + + SECTION("number") + { + json j(17); + json::iterator it = j.begin(); + CHECK(it->type_name() == "number"); + it = j.end(); + CHECK_THROWS_AS(it->type_name(), std::out_of_range); + CHECK_THROWS_WITH(it->type_name(), "cannot get value"); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::iterator it = j.begin(); + CHECK(it->type_name() == "string"); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::iterator it = j.begin(); + CHECK(it->type_name() == "number"); + } + } + } + + SECTION("increment/decrement") + { + SECTION("post-increment") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it = j.begin(); + CHECK(it.m_it.primitive_iterator == 1); + it++; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("number") + { + json j(17); + json::iterator it = j.begin(); + CHECK(it.m_it.primitive_iterator == 0); + it++; + CHECK(it.m_it.primitive_iterator == 1); + it++; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::iterator it = j.begin(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + it++; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::iterator it = j.begin(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + } + } + + SECTION("pre-increment") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it = j.begin(); + CHECK(it.m_it.primitive_iterator == 1); + ++it; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("number") + { + json j(17); + json::iterator it = j.begin(); + CHECK(it.m_it.primitive_iterator == 0); + ++it; + CHECK(it.m_it.primitive_iterator == 1); + ++it; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::iterator it = j.begin(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + ++it; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::iterator it = j.begin(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + } + } + + SECTION("post-decrement") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it = j.end(); + CHECK(it.m_it.primitive_iterator == 1); + } + + SECTION("number") + { + json j(17); + json::iterator it = j.end(); + CHECK(it.m_it.primitive_iterator == 1); + it--; + CHECK(it.m_it.primitive_iterator == 0); + it--; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::iterator it = j.end(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + it--; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::iterator it = j.end(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + } + } + + SECTION("pre-decrement") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it = j.end(); + CHECK(it.m_it.primitive_iterator == 1); + } + + SECTION("number") + { + json j(17); + json::iterator it = j.end(); + CHECK(it.m_it.primitive_iterator == 1); + --it; + CHECK(it.m_it.primitive_iterator == 0); + --it; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::iterator it = j.end(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + --it; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::iterator it = j.end(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + } + } + } +} diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp new file mode 100644 index 00000000..708a8cbf --- /dev/null +++ b/test/src/unit-class_lexer.cpp @@ -0,0 +1,155 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("lexer class") +{ + SECTION("scan") + { + SECTION("structural characters") + { + CHECK(json::lexer("[").scan() == json::lexer::token_type::begin_array); + CHECK(json::lexer("]").scan() == json::lexer::token_type::end_array); + CHECK(json::lexer("{").scan() == json::lexer::token_type::begin_object); + CHECK(json::lexer("}").scan() == json::lexer::token_type::end_object); + CHECK(json::lexer(",").scan() == json::lexer::token_type::value_separator); + CHECK(json::lexer(":").scan() == json::lexer::token_type::name_separator); + } + + SECTION("literal names") + { + CHECK(json::lexer("null").scan() == json::lexer::token_type::literal_null); + CHECK(json::lexer("true").scan() == json::lexer::token_type::literal_true); + CHECK(json::lexer("false").scan() == json::lexer::token_type::literal_false); + } + + SECTION("numbers") + { + CHECK(json::lexer("0").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("1").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("2").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("3").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("4").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("5").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("6").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("7").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("8").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("9").scan() == json::lexer::token_type::value_number); + } + + SECTION("whitespace") + { + // result is end_of_input, because not token is following + CHECK(json::lexer(" ").scan() == json::lexer::token_type::end_of_input); + CHECK(json::lexer("\t").scan() == json::lexer::token_type::end_of_input); + CHECK(json::lexer("\n").scan() == json::lexer::token_type::end_of_input); + CHECK(json::lexer("\r").scan() == json::lexer::token_type::end_of_input); + CHECK(json::lexer(" \t\n\r\n\t ").scan() == json::lexer::token_type::end_of_input); + } + } + + SECTION("token_type_name") + { + CHECK(json::lexer::token_type_name(json::lexer::token_type::uninitialized) == ""); + CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_true) == "true literal"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_false) == "false literal"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_null) == "null literal"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::value_string) == "string literal"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::value_number) == "number literal"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_array) == "'['"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_object) == "'{'"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::end_array) == "']'"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::end_object) == "'}'"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::name_separator) == "':'"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::value_separator) == "','"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::parse_error) == ""); + CHECK(json::lexer::token_type_name(json::lexer::token_type::end_of_input) == "end of input"); + } + + SECTION("parse errors on first character") + { + for (int c = 1; c < 128; ++c) + { + auto s = std::string(1, c); + + switch (c) + { + // single characters that are valid tokens + case ('['): + case (']'): + case ('{'): + case ('}'): + case (','): + case (':'): + case ('0'): + case ('1'): + case ('2'): + case ('3'): + case ('4'): + case ('5'): + case ('6'): + case ('7'): + case ('8'): + case ('9'): + { + CHECK(json::lexer(s.c_str()).scan() != json::lexer::token_type::parse_error); + break; + } + + // whitespace + case (' '): + case ('\t'): + case ('\n'): + case ('\r'): + { + CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::end_of_input); + break; + } + + // anything else is not expected + default: + { + CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::parse_error); + break; + } + } + } + } + + SECTION("to_unicode") + { + CHECK(json::lexer::to_unicode(0x1F4A9) == "💩"); + CHECK_THROWS_AS(json::lexer::to_unicode(0x200000), std::out_of_range); + CHECK_THROWS_WITH(json::lexer::to_unicode(0x200000), "code points above 0x10FFFF are invalid"); + } +} diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp new file mode 100644 index 00000000..fe005503 --- /dev/null +++ b/test/src/unit-class_parser.cpp @@ -0,0 +1,753 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("parser class") +{ + SECTION("parse") + { + SECTION("null") + { + CHECK(json::parser("null").parse() == json(nullptr)); + } + + SECTION("true") + { + CHECK(json::parser("true").parse() == json(true)); + } + + SECTION("false") + { + CHECK(json::parser("false").parse() == json(false)); + } + + SECTION("array") + { + SECTION("empty array") + { + CHECK(json::parser("[]").parse() == json(json::value_t::array)); + CHECK(json::parser("[ ]").parse() == json(json::value_t::array)); + } + + SECTION("nonempty array") + { + CHECK(json::parser("[true, false, null]").parse() == json({true, false, nullptr})); + } + } + + SECTION("object") + { + SECTION("empty object") + { + CHECK(json::parser("{}").parse() == json(json::value_t::object)); + CHECK(json::parser("{ }").parse() == json(json::value_t::object)); + } + + SECTION("nonempty object") + { + CHECK(json::parser("{\"\": true, \"one\": 1, \"two\": null}").parse() == json({{"", true}, {"one", 1}, {"two", nullptr}})); + } + } + + SECTION("string") + { + // empty string + CHECK(json::parser("\"\"").parse() == json(json::value_t::string)); + + SECTION("errors") + { + // error: tab in string + CHECK_THROWS_AS(json::parser("\"\t\"").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("\"\t\"").parse(), "parse error - unexpected '\"'"); + // error: newline in string + CHECK_THROWS_AS(json::parser("\"\n\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\r\"").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("\"\n\"").parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\r\"").parse(), "parse error - unexpected '\"'"); + // error: backspace in string + CHECK_THROWS_AS(json::parser("\"\b\"").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("\"\b\"").parse(), "parse error - unexpected '\"'"); + // improve code coverage + CHECK_THROWS_AS(json::parser("\uFF01").parse(), std::invalid_argument); + // unescaped control characters + CHECK_THROWS_AS(json::parser("\"\x00\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x01\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x02\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x03\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x04\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x05\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x06\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x07\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x08\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x09\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x0a\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x0b\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x0c\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x0d\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x0e\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x0f\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x10\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x11\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x12\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x13\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x14\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x15\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x16\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x17\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x18\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x19\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x1a\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x1b\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x1c\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x1d\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x1e\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x1f\"").parse(), std::invalid_argument); + } + + SECTION("escaped") + { + // quotation mark "\"" + auto r1 = R"("\"")"_json; + CHECK(json::parser("\"\\\"\"").parse() == r1); + // reverse solidus "\\" + auto r2 = R"("\\")"_json; + CHECK(json::parser("\"\\\\\"").parse() == r2); + // solidus + CHECK(json::parser("\"\\/\"").parse() == R"("/")"_json); + // backspace + CHECK(json::parser("\"\\b\"").parse() == json("\b")); + // formfeed + CHECK(json::parser("\"\\f\"").parse() == json("\f")); + // newline + CHECK(json::parser("\"\\n\"").parse() == json("\n")); + // carriage return + CHECK(json::parser("\"\\r\"").parse() == json("\r")); + // horizontal tab + CHECK(json::parser("\"\\t\"").parse() == json("\t")); + + CHECK(json::parser("\"\\u0001\"").parse().get() == "\x01"); + CHECK(json::parser("\"\\u000a\"").parse().get() == "\n"); + CHECK(json::parser("\"\\u00b0\"").parse().get() == "°"); + CHECK(json::parser("\"\\u0c00\"").parse().get() == "ఀ"); + CHECK(json::parser("\"\\ud000\"").parse().get() == "퀀"); + CHECK(json::parser("\"\\u000E\"").parse().get() == "\x0E"); + CHECK(json::parser("\"\\u00F0\"").parse().get() == "ð"); + CHECK(json::parser("\"\\u0100\"").parse().get() == "Ā"); + CHECK(json::parser("\"\\u2000\"").parse().get() == " "); + CHECK(json::parser("\"\\uFFFF\"").parse().get() == "￿"); + CHECK(json::parser("\"\\u20AC\"").parse().get() == "€"); + CHECK(json::parser("\"€\"").parse().get() == "€"); + CHECK(json::parser("\"🎈\"").parse().get() == "🎈"); + + CHECK(json::parse("\"\\ud80c\\udc60\"").get() == u8"\U00013060"); + CHECK(json::parse("\"\\ud83c\\udf1e\"").get() == "🌞"); + } + } + + SECTION("number") + { + SECTION("integers") + { + SECTION("without exponent") + { + CHECK(json::parser("-128").parse() == json(-128)); + CHECK(json::parser("-0").parse() == json(-0)); + CHECK(json::parser("0").parse() == json(0)); + CHECK(json::parser("128").parse() == json(128)); + } + + SECTION("with exponent") + { + CHECK(json::parser("0e1").parse() == json(0e1)); + CHECK(json::parser("0E1").parse() == json(0e1)); + + CHECK(json::parser("10000E-4").parse() == json(10000e-4)); + CHECK(json::parser("10000E-3").parse() == json(10000e-3)); + CHECK(json::parser("10000E-2").parse() == json(10000e-2)); + CHECK(json::parser("10000E-1").parse() == json(10000e-1)); + CHECK(json::parser("10000E0").parse() == json(10000e0)); + CHECK(json::parser("10000E1").parse() == json(10000e1)); + CHECK(json::parser("10000E2").parse() == json(10000e2)); + CHECK(json::parser("10000E3").parse() == json(10000e3)); + CHECK(json::parser("10000E4").parse() == json(10000e4)); + + CHECK(json::parser("10000e-4").parse() == json(10000e-4)); + CHECK(json::parser("10000e-3").parse() == json(10000e-3)); + CHECK(json::parser("10000e-2").parse() == json(10000e-2)); + CHECK(json::parser("10000e-1").parse() == json(10000e-1)); + CHECK(json::parser("10000e0").parse() == json(10000e0)); + CHECK(json::parser("10000e1").parse() == json(10000e1)); + CHECK(json::parser("10000e2").parse() == json(10000e2)); + CHECK(json::parser("10000e3").parse() == json(10000e3)); + CHECK(json::parser("10000e4").parse() == json(10000e4)); + + CHECK(json::parser("-0e1").parse() == json(-0e1)); + CHECK(json::parser("-0E1").parse() == json(-0e1)); + CHECK(json::parser("-0E123").parse() == json(-0e123)); + } + + SECTION("edge cases") + { + // From RFC7159, Section 6: + // Note that when such software is used, numbers that are + // integers and are in the range [-(2**53)+1, (2**53)-1] + // are interoperable in the sense that implementations will + // agree exactly on their numeric values. + + // -(2**53)+1 + CHECK(json::parser("-9007199254740991").parse().get() == -9007199254740991); + // (2**53)-1 + CHECK(json::parser("9007199254740991").parse().get() == 9007199254740991); + } + + SECTION("over the edge cases") // issue #178 - Integer conversion to unsigned (incorrect handling of 64 bit integers) + { + // While RFC7159, Section 6 specifies a preference for support + // for ranges in range of IEEE 754-2008 binary64 (double precision) + // this does not accommodate 64 bit integers without loss of accuracy. + // As 64 bit integers are now widely used in software, it is desirable + // to expand support to to the full 64 bit (signed and unsigned) range + // i.e. -(2**63) -> (2**64)-1. + + // -(2**63) ** Note: compilers see negative literals as negated positive numbers (hence the -1)) + CHECK(json::parser("-9223372036854775808").parse().get() == -9223372036854775807 - 1); + // (2**63)-1 + CHECK(json::parser("9223372036854775807").parse().get() == 9223372036854775807); + // (2**64)-1 + CHECK(json::parser("18446744073709551615").parse().get() == 18446744073709551615u); + } + } + + SECTION("floating-point") + { + SECTION("without exponent") + { + CHECK(json::parser("-128.5").parse() == json(-128.5)); + CHECK(json::parser("0.999").parse() == json(0.999)); + CHECK(json::parser("128.5").parse() == json(128.5)); + CHECK(json::parser("-0.0").parse() == json(-0.0)); + } + + SECTION("with exponent") + { + CHECK(json::parser("-128.5E3").parse() == json(-128.5E3)); + CHECK(json::parser("-128.5E-3").parse() == json(-128.5E-3)); + CHECK(json::parser("-0.0e1").parse() == json(-0.0e1)); + CHECK(json::parser("-0.0E1").parse() == json(-0.0e1)); + } + } + + SECTION("invalid numbers") + { + CHECK_THROWS_AS(json::parser("01").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("--1").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1.").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1E").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1E-").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1.E1").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-1E").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0E#").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0E-#").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0#").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0.0:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0.0Z").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0E123:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0e0-:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0e-:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0f").parse(), std::invalid_argument); + + // numbers must not begin with "+" + CHECK_THROWS_AS(json::parser("+1").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("+0").parse(), std::invalid_argument); + + CHECK_THROWS_WITH(json::parser("01").parse(), + "parse error - unexpected number literal; expected end of input"); + CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'"); + CHECK_THROWS_WITH(json::parser("1.").parse(), + "parse error - unexpected '.'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1E").parse(), + "parse error - unexpected 'E'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1E-").parse(), + "parse error - unexpected 'E'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1.E1").parse(), + "parse error - unexpected '.'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-1E").parse(), + "parse error - unexpected 'E'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0E#").parse(), + "parse error - unexpected 'E'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0E-#").parse(), + "parse error - unexpected 'E'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0#").parse(), + "parse error - unexpected '#'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0.0:").parse(), + "parse error - unexpected ':'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0.0Z").parse(), + "parse error - unexpected 'Z'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0E123:").parse(), + "parse error - unexpected ':'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0e0-:").parse(), + "parse error - unexpected '-'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0e-:").parse(), + "parse error - unexpected 'e'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0f").parse(), + "parse error - unexpected 'f'; expected end of input"); + } + } + } + + SECTION("parse errors") + { + // unexpected end of number + CHECK_THROWS_AS(json::parser("0.").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("--").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0.").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-.").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("0.:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("e.").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1e.").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1e/").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1e:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1E.").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1E/").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1E:").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("0.").parse(), + "parse error - unexpected '.'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-").parse(), "parse error - unexpected '-'"); + CHECK_THROWS_WITH(json::parser("--").parse(), + "parse error - unexpected '-'"); + CHECK_THROWS_WITH(json::parser("-0.").parse(), + "parse error - unexpected '.'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-.").parse(), + "parse error - unexpected '-'"); + CHECK_THROWS_WITH(json::parser("-:").parse(), + "parse error - unexpected '-'"); + CHECK_THROWS_WITH(json::parser("0.:").parse(), + "parse error - unexpected '.'; expected end of input"); + CHECK_THROWS_WITH(json::parser("e.").parse(), + "parse error - unexpected 'e'"); + CHECK_THROWS_WITH(json::parser("1e.").parse(), + "parse error - unexpected 'e'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1e/").parse(), + "parse error - unexpected 'e'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1e:").parse(), + "parse error - unexpected 'e'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1E.").parse(), + "parse error - unexpected 'E'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1E/").parse(), + "parse error - unexpected 'E'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1E:").parse(), + "parse error - unexpected 'E'; expected end of input"); + + // unexpected end of null + CHECK_THROWS_AS(json::parser("n").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("nu").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("nul").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("n").parse(), "parse error - unexpected 'n'"); + CHECK_THROWS_WITH(json::parser("nu").parse(), + "parse error - unexpected 'n'"); + CHECK_THROWS_WITH(json::parser("nul").parse(), + "parse error - unexpected 'n'"); + + // unexpected end of true + CHECK_THROWS_AS(json::parser("t").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("tr").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("tru").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("t").parse(), "parse error - unexpected 't'"); + CHECK_THROWS_WITH(json::parser("tr").parse(), + "parse error - unexpected 't'"); + CHECK_THROWS_WITH(json::parser("tru").parse(), + "parse error - unexpected 't'"); + + // unexpected end of false + CHECK_THROWS_AS(json::parser("f").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("fa").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("fal").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("fals").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("f").parse(), "parse error - unexpected 'f'"); + CHECK_THROWS_WITH(json::parser("fa").parse(), + "parse error - unexpected 'f'"); + CHECK_THROWS_WITH(json::parser("fal").parse(), + "parse error - unexpected 'f'"); + CHECK_THROWS_WITH(json::parser("fals").parse(), + "parse error - unexpected 'f'"); + + // missing/unexpected end of array + CHECK_THROWS_AS(json::parser("[").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("[1").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("[1,").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("[1,]").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("]").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("[").parse(), + "parse error - unexpected end of input"); + CHECK_THROWS_WITH(json::parser("[1").parse(), + "parse error - unexpected end of input; expected ']'"); + CHECK_THROWS_WITH(json::parser("[1,").parse(), + "parse error - unexpected end of input"); + CHECK_THROWS_WITH(json::parser("[1,]").parse(), + "parse error - unexpected ']'"); + CHECK_THROWS_WITH(json::parser("]").parse(), "parse error - unexpected ']'"); + + // missing/unexpected end of object + CHECK_THROWS_AS(json::parser("{").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("{\"foo\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("{\"foo\":").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("{\"foo\":}").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("{\"foo\":1,}").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("}").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("{").parse(), + "parse error - unexpected end of input; expected string literal"); + CHECK_THROWS_WITH(json::parser("{\"foo\"").parse(), + "parse error - unexpected end of input; expected ':'"); + CHECK_THROWS_WITH(json::parser("{\"foo\":").parse(), + "parse error - unexpected end of input"); + CHECK_THROWS_WITH(json::parser("{\"foo\":}").parse(), + "parse error - unexpected '}'"); + CHECK_THROWS_WITH(json::parser("{\"foo\":1,}").parse(), + "parse error - unexpected '}'; expected string literal"); + CHECK_THROWS_WITH(json::parser("}").parse(), "parse error - unexpected '}'"); + + // missing/unexpected end of string + CHECK_THROWS_AS(json::parser("\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u0\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u01\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u012\"").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("\"").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\\"").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u\"").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u0\"").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u01\"").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u012\"").parse(), + "parse error - unexpected '\"'"); + + // invalid escapes + for (int c = 1; c < 128; ++c) + { + auto s = std::string("\"\\") + std::string(1, c) + "\""; + + switch (c) + { + // valid escapes + case ('"'): + case ('\\'): + case ('/'): + case ('b'): + case ('f'): + case ('n'): + case ('r'): + case ('t'): + { + CHECK_NOTHROW(json::parser(s).parse()); + break; + } + + // \u must be followed with four numbers, so we skip it here + case ('u'): + { + break; + } + + // any other combination of backslash and character is invalid + default: + { + CHECK_THROWS_AS(json::parser(s).parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser(s).parse(), "parse error - unexpected '\"'"); + break; + } + } + } + + // invalid \uxxxx escapes + { + // check whether character is a valid hex character + const auto valid = [](int c) + { + switch (c) + { + case ('0'): + case ('1'): + case ('2'): + case ('3'): + case ('4'): + case ('5'): + case ('6'): + case ('7'): + case ('8'): + case ('9'): + case ('a'): + case ('b'): + case ('c'): + case ('d'): + case ('e'): + case ('f'): + case ('A'): + case ('B'): + case ('C'): + case ('D'): + case ('E'): + case ('F'): + { + return true; + } + + default: + { + return false; + } + } + }; + + for (int c = 1; c < 128; ++c) + { + std::string s = "\"\\u"; + + // create a string with the iterated character at each position + auto s1 = s + "000" + std::string(1, c) + "\""; + auto s2 = s + "00" + std::string(1, c) + "0\""; + auto s3 = s + "0" + std::string(1, c) + "00\""; + auto s4 = s + std::string(1, c) + "000\""; + + if (valid(c)) + { + CHECK_NOTHROW(json::parser(s1).parse()); + CHECK_NOTHROW(json::parser(s2).parse()); + CHECK_NOTHROW(json::parser(s3).parse()); + CHECK_NOTHROW(json::parser(s4).parse()); + } + else + { + CHECK_THROWS_AS(json::parser(s1).parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser(s2).parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser(s3).parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser(s4).parse(), std::invalid_argument); + + CHECK_THROWS_WITH(json::parser(s1).parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser(s2).parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser(s3).parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser(s4).parse(), "parse error - unexpected '\"'"); + } + } + } + + // missing part of a surrogate pair + CHECK_THROWS_AS(json::parse("\"\\uD80C\""), std::invalid_argument); + CHECK_THROWS_WITH(json::parse("\"\\uD80C\""), "missing low surrogate"); + // invalid surrogate pair + CHECK_THROWS_AS(json::parse("\"\\uD80C\\uD80C\""), std::invalid_argument); + CHECK_THROWS_AS(json::parse("\"\\uD80C\\u0000\""), std::invalid_argument); + CHECK_THROWS_AS(json::parse("\"\\uD80C\\uFFFF\""), std::invalid_argument); + CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uD80C\""), + "missing or wrong low surrogate"); + CHECK_THROWS_WITH(json::parse("\"\\uD80C\\u0000\""), + "missing or wrong low surrogate"); + CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uFFFF\""), + "missing or wrong low surrogate"); + } + + SECTION("callback function") + { + auto s_object = R"( + { + "foo": 2, + "bar": { + "baz": 1 + } + } + )"; + + auto s_array = R"( + [1,2,[3,4,5],4,5] + )"; + + SECTION("filter nothing") + { + json j_object = json::parse(s_object, [](int, json::parse_event_t, const json&) + { + return true; + }); + + CHECK (j_object == json({{"foo", 2}, {"bar", {{"baz", 1}}}})); + + json j_array = json::parse(s_array, [](int, json::parse_event_t, const json&) + { + return true; + }); + + CHECK (j_array == json({1, 2, {3, 4, 5}, 4, 5})); + } + + SECTION("filter everything") + { + json j_object = json::parse(s_object, [](int, json::parse_event_t, const json&) + { + return false; + }); + + // the top-level object will be discarded, leaving a null + CHECK (j_object.is_null()); + + json j_array = json::parse(s_array, [](int, json::parse_event_t, const json&) + { + return false; + }); + + // the top-level array will be discarded, leaving a null + CHECK (j_array.is_null()); + } + + SECTION("filter specific element") + { + json j_object = json::parse(s_object, [](int, json::parse_event_t, const json & j) + { + // filter all number(2) elements + if (j == json(2)) + { + return false; + } + else + { + return true; + } + }); + + CHECK (j_object == json({{"bar", {{"baz", 1}}}})); + + json j_array = json::parse(s_array, [](int, json::parse_event_t, const json & j) + { + if (j == json(2)) + { + return false; + } + else + { + return true; + } + }); + + CHECK (j_array == json({1, {3, 4, 5}, 4, 5})); + } + + SECTION("filter specific events") + { + SECTION("first closing event") + { + { + json j_object = json::parse(s_object, [](int, json::parse_event_t e, const json&) + { + static bool first = true; + if (e == json::parse_event_t::object_end and first) + { + first = false; + return false; + } + else + { + return true; + } + }); + + // the first completed object will be discarded + CHECK (j_object == json({{"foo", 2}})); + } + + { + json j_array = json::parse(s_array, [](int, json::parse_event_t e, const json&) + { + static bool first = true; + if (e == json::parse_event_t::array_end and first) + { + first = false; + return false; + } + else + { + return true; + } + }); + + // the first completed array will be discarded + CHECK (j_array == json({1, 2, 4, 5})); + } + } + } + + SECTION("special cases") + { + // the following test cases cover the situation in which an empty + // object and array is discarded only after the closing character + // has been read + + json j_empty_object = json::parse("{}", [](int, json::parse_event_t e, const json&) + { + if (e == json::parse_event_t::object_end) + { + return false; + } + else + { + return true; + } + }); + CHECK(j_empty_object == json()); + + json j_empty_array = json::parse("[]", [](int, json::parse_event_t e, const json&) + { + if (e == json::parse_event_t::array_end) + { + return false; + } + else + { + return true; + } + }); + CHECK(j_empty_array == json()); + } + } + + SECTION("copy constructor") + { + json::string_t* s = new json::string_t("[1,2,3,4]"); + json::parser p(*s); + delete s; + CHECK(p.parse() == json({1, 2, 3, 4})); + } +} diff --git a/test/src/unit-comparison.cpp b/test/src/unit-comparison.cpp new file mode 100644 index 00000000..a8ce4c8a --- /dev/null +++ b/test/src/unit-comparison.cpp @@ -0,0 +1,246 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("lexicographical comparison operators") +{ + SECTION("types") + { + std::vector j_types = + { + json::value_t::null, + json::value_t::boolean, + json::value_t::number_integer, + json::value_t::number_unsigned, + json::value_t::number_float, + json::value_t::object, + json::value_t::array, + json::value_t::string + }; + + SECTION("comparison: less") + { + std::vector> expected = + { + {false, true, true, true, true, true, true, true}, + {false, false, true, true, true, true, true, true}, + {false, false, false, false, false, true, true, true}, + {false, false, false, false, false, true, true, true}, + {false, false, false, false, false, true, true, true}, + {false, false, false, false, false, false, true, true}, + {false, false, false, false, false, false, false, true}, + {false, false, false, false, false, false, false, false} + }; + + for (size_t i = 0; i < j_types.size(); ++i) + { + for (size_t j = 0; j < j_types.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check precomputed values + CHECK(operator<(j_types[i], j_types[j]) == expected[i][j]); + } + } + } + } + + SECTION("values") + { + json j_values = + { + nullptr, nullptr, + 17, 42, + 8u, 13u, + 3.14159, 23.42, + "foo", "bar", + true, false, + {1, 2, 3}, {"one", "two", "three"}, + {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}} + }; + + SECTION("comparison: equal") + { + std::vector> expected = + { + {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true} + }; + + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check precomputed values + CHECK( (j_values[i] == j_values[j]) == expected[i][j] ); + } + } + + // comparison with discarded elements + json j_discarded(json::value_t::discarded); + for (size_t i = 0; i < j_values.size(); ++i) + { + CHECK( (j_values[i] == j_discarded) == false); + CHECK( (j_discarded == j_values[i]) == false); + CHECK( (j_discarded == j_discarded) == false); + } + + // compare with null pointer + json j_null; + CHECK(j_null == nullptr); + CHECK(nullptr == j_null); + } + + SECTION("comparison: not equal") + { + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check definition + CHECK( (j_values[i] != j_values[j]) == not(j_values[i] == j_values[j]) ); + } + } + + // compare with null pointer + json j_null; + CHECK( (j_null != nullptr) == false); + CHECK( (nullptr != j_null) == false); + CHECK( (j_null != nullptr) == not(j_null == nullptr)); + CHECK( (nullptr != j_null) == not(nullptr == j_null)); + } + + SECTION("comparison: less") + { + std::vector> expected = + { + {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, + {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, + {false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true}, + {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true}, + {false, false, true, true, false, true, false, true, true, true, false, false, true, true, true, true}, + {false, false, true, true, false, false, false, true, true, true, false, false, true, true, true, true}, + {false, false, true, true, true, true, false, true, true, true, false, false, true, true, true, true}, + {false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, + {false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true}, + {false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true}, + {false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false}, + {false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false}, + {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false} + }; + + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check precomputed values + CHECK( (j_values[i] < j_values[j]) == expected[i][j] ); + } + } + + // comparison with discarded elements + json j_discarded(json::value_t::discarded); + for (size_t i = 0; i < j_values.size(); ++i) + { + CAPTURE(i); + CHECK( (j_values[i] < j_discarded) == false); + CHECK( (j_discarded < j_values[i]) == false); + CHECK( (j_discarded < j_discarded) == false); + } + } + + SECTION("comparison: less than or equal equal") + { + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check definition + CHECK( (j_values[i] <= j_values[j]) == not(j_values[j] < j_values[i]) ); + } + } + } + + SECTION("comparison: greater than") + { + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check definition + CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) ); + } + } + } + + SECTION("comparison: greater than or equal") + { + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check definition + CHECK( (j_values[i] >= j_values[j]) == not(j_values[i] < j_values[j]) ); + } + } + } + } +} diff --git a/test/src/unit-concepts.cpp b/test/src/unit-concepts.cpp new file mode 100644 index 00000000..9cbdd8e9 --- /dev/null +++ b/test/src/unit-concepts.cpp @@ -0,0 +1,169 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("concepts") +{ + SECTION("container requirements for json") + { + // X: container class: json + // T: type of objects: json + // a, b: values of type X: json + + // TABLE 96 - Container Requirements + + // X::value_type must return T + CHECK((std::is_same::value)); + + // X::reference must return lvalue of T + CHECK((std::is_same::value)); + + // X::const_reference must return const lvalue of T + CHECK((std::is_same::value)); + + // X::iterator must return iterator whose value_type is T + CHECK((std::is_same::value)); + // X::iterator must meet the forward iterator requirements + CHECK((std::is_base_of::iterator_category>::value)); + // X::iterator must be convertible to X::const_iterator + CHECK((std::is_convertible::value)); + + // X::const_iterator must return iterator whose value_type is T + CHECK((std::is_same::value)); + // X::const_iterator must meet the forward iterator requirements + CHECK((std::is_base_of::iterator_category>::value)); + + // X::difference_type must return a signed integer + CHECK((std::is_signed::value)); + // X::difference_type must be identical to X::iterator::difference_type + CHECK((std::is_same::value)); + // X::difference_type must be identical to X::const_iterator::difference_type + CHECK((std::is_same::value)); + + // X::size_type must return an unsigned integer + CHECK((std::is_unsigned::value)); + // X::size_type can represent any non-negative value of X::difference_type + CHECK(std::numeric_limits::max() <= + std::numeric_limits::max()); + + // the expression "X u" has the post-condition "u.empty()" + { + json u; + CHECK(u.empty()); + } + + // the expression "X()" has the post-condition "X().empty()" + CHECK(json().empty()); + } + + SECTION("class json") + { + SECTION("DefaultConstructible") + { + CHECK(std::is_nothrow_default_constructible::value); + } + + SECTION("MoveConstructible") + { + CHECK(std::is_nothrow_move_constructible::value); + } + + SECTION("CopyConstructible") + { + CHECK(std::is_copy_constructible::value); + } + + SECTION("MoveAssignable") + { + CHECK(std::is_nothrow_move_assignable::value); + } + + SECTION("CopyAssignable") + { + CHECK(std::is_copy_assignable::value); + } + + SECTION("Destructible") + { + CHECK(std::is_nothrow_destructible::value); + } + + SECTION("StandardLayoutType") + { + CHECK(std::is_standard_layout::value); + } + } + + SECTION("class iterator") + { + SECTION("CopyConstructible") + { + CHECK(std::is_nothrow_copy_constructible::value); + CHECK(std::is_nothrow_copy_constructible::value); + } + + SECTION("CopyAssignable") + { + // STL iterators used by json::iterator don't pass this test in Debug mode +#if !defined(_MSC_VER) || (_ITERATOR_DEBUG_LEVEL == 0) + CHECK(std::is_nothrow_copy_assignable::value); + CHECK(std::is_nothrow_copy_assignable::value); +#endif + } + + SECTION("Destructible") + { + CHECK(std::is_nothrow_destructible::value); + CHECK(std::is_nothrow_destructible::value); + } + + SECTION("Swappable") + { + { + json j {1, 2, 3}; + json::iterator it1 = j.begin(); + json::iterator it2 = j.end(); + std::swap(it1, it2); + CHECK(it1 == j.end()); + CHECK(it2 == j.begin()); + } + { + json j {1, 2, 3}; + json::const_iterator it1 = j.cbegin(); + json::const_iterator it2 = j.cend(); + std::swap(it1, it2); + CHECK(it1 == j.end()); + CHECK(it2 == j.begin()); + } + } + } +} diff --git a/test/src/unit-constructor.cpp b/test/src/unit-constructor.cpp new file mode 100644 index 00000000..e4842a58 --- /dev/null +++ b/test/src/unit-constructor.cpp @@ -0,0 +1,1470 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +#include +#include +#include +#include +#include +#include + +TEST_CASE("constructors") +{ + SECTION("create an empty value with a given type") + { + SECTION("null") + { + auto t = json::value_t::null; + json j(t); + CHECK(j.type() == t); + } + + SECTION("discarded") + { + auto t = json::value_t::discarded; + json j(t); + CHECK(j.type() == t); + } + + SECTION("object") + { + auto t = json::value_t::object; + json j(t); + CHECK(j.type() == t); + } + + SECTION("array") + { + auto t = json::value_t::array; + json j(t); + CHECK(j.type() == t); + } + + SECTION("boolean") + { + auto t = json::value_t::boolean; + json j(t); + CHECK(j.type() == t); + } + + SECTION("string") + { + auto t = json::value_t::string; + json j(t); + CHECK(j.type() == t); + } + + SECTION("number_integer") + { + auto t = json::value_t::number_integer; + json j(t); + CHECK(j.type() == t); + } + + SECTION("number_unsigned") + { + auto t = json::value_t::number_unsigned; + json j(t); + CHECK(j.type() == t); + } + + SECTION("number_float") + { + auto t = json::value_t::number_float; + json j(t); + CHECK(j.type() == t); + } + } + + SECTION("create a null object (implicitly)") + { + SECTION("no parameter") + { + json j{}; + CHECK(j.type() == json::value_t::null); + } + } + + SECTION("create a null object (explicitly)") + { + SECTION("parameter") + { + json j(nullptr); + CHECK(j.type() == json::value_t::null); + } + } + + SECTION("create an object (explicit)") + { + SECTION("empty object") + { + json::object_t o; + json j(o); + CHECK(j.type() == json::value_t::object); + } + + SECTION("filled object") + { + json::object_t o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; + json j(o); + CHECK(j.type() == json::value_t::object); + } + } + + SECTION("create an object (implicit)") + { + // reference object + json::object_t o_reference {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; + json j_reference(o_reference); + + SECTION("std::map") + { + std::map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; + json j(o); + CHECK(j.type() == json::value_t::object); + CHECK(j == j_reference); + } + + SECTION("std::map") + { + std::map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; + json j(o); + CHECK(j.type() == json::value_t::object); + CHECK(j == j_reference); + } + + SECTION("std::multimap") + { + std::multimap o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; + json j(o); + CHECK(j.type() == json::value_t::object); + CHECK(j == j_reference); + } + + SECTION("std::unordered_map") + { + std::unordered_map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; + json j(o); + CHECK(j.type() == json::value_t::object); + CHECK(j == j_reference); + } + + SECTION("std::unordered_multimap") + { + std::unordered_multimap o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; + json j(o); + CHECK(j.type() == json::value_t::object); + CHECK(j == j_reference); + } + + SECTION("associative container literal") + { + json j({{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}); + CHECK(j.type() == json::value_t::object); + CHECK(j == j_reference); + } + } + + SECTION("create an array (explicit)") + { + SECTION("empty array") + { + json::array_t a; + json j(a); + CHECK(j.type() == json::value_t::array); + } + + SECTION("filled array") + { + json::array_t a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a); + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("create an array (implicit)") + { + // reference array + json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j_reference(a_reference); + + SECTION("std::list") + { + std::list a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a); + CHECK(j.type() == json::value_t::array); + CHECK(j == j_reference); + } + + SECTION("std::forward_list") + { + std::forward_list a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a); + CHECK(j.type() == json::value_t::array); + CHECK(j == j_reference); + } + + SECTION("std::array") + { + std::array a {{json(1), json(1u), json(2.2), json(false), json("string"), json()}}; + json j(a); + CHECK(j.type() == json::value_t::array); + CHECK(j == j_reference); + } + + SECTION("std::vector") + { + std::vector a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a); + CHECK(j.type() == json::value_t::array); + CHECK(j == j_reference); + } + + SECTION("std::deque") + { + std::deque a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a); + CHECK(j.type() == json::value_t::array); + CHECK(j == j_reference); + } + + SECTION("std::set") + { + std::set a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a); + CHECK(j.type() == json::value_t::array); + // we cannot really check for equality here + } + + SECTION("std::unordered_set") + { + std::unordered_set a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a); + CHECK(j.type() == json::value_t::array); + // we cannot really check for equality here + } + + SECTION("sequence container literal") + { + json j({json(1), json(1u), json(2.2), json(false), json("string"), json()}); + CHECK(j.type() == json::value_t::array); + CHECK(j == j_reference); + } + } + + SECTION("create a string (explicit)") + { + SECTION("empty string") + { + json::string_t s; + json j(s); + CHECK(j.type() == json::value_t::string); + } + + SECTION("filled string") + { + json::string_t s {"Hello world"}; + json j(s); + CHECK(j.type() == json::value_t::string); + } + } + + SECTION("create a string (implicit)") + { + // reference string + json::string_t s_reference {"Hello world"}; + json j_reference(s_reference); + + SECTION("std::string") + { + std::string s {"Hello world"}; + json j(s); + CHECK(j.type() == json::value_t::string); + CHECK(j == j_reference); + } + + SECTION("char[]") + { + char s[] {"Hello world"}; + json j(s); + CHECK(j.type() == json::value_t::string); + CHECK(j == j_reference); + } + + SECTION("const char*") + { + const char* s {"Hello world"}; + json j(s); + CHECK(j.type() == json::value_t::string); + CHECK(j == j_reference); + } + + SECTION("string literal") + { + json j("Hello world"); + CHECK(j.type() == json::value_t::string); + CHECK(j == j_reference); + } + } + + SECTION("create a boolean (explicit)") + { + SECTION("empty boolean") + { + json::boolean_t b{}; + json j(b); + CHECK(j.type() == json::value_t::boolean); + } + + SECTION("filled boolean (true)") + { + json j(true); + CHECK(j.type() == json::value_t::boolean); + } + + SECTION("filled boolean (false)") + { + json j(false); + CHECK(j.type() == json::value_t::boolean); + } + } + + SECTION("create an integer number (explicit)") + { + SECTION("uninitialized value") + { + json::number_integer_t n{}; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + } + + SECTION("initialized value") + { + json::number_integer_t n(42); + json j(n); + CHECK(j.type() == json::value_t::number_integer); + } + } + + SECTION("create an integer number (implicit)") + { + // reference objects + json::number_integer_t n_reference = 42; + json j_reference(n_reference); + json::number_unsigned_t n_unsigned_reference = 42; + json j_unsigned_reference(n_unsigned_reference); + + SECTION("short") + { + short n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("unsigned short") + { + unsigned short n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("int") + { + int n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("unsigned int") + { + unsigned int n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("long") + { + long n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("unsigned long") + { + unsigned long n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("long long") + { + long long n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("unsigned long long") + { + unsigned long long n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("int8_t") + { + int8_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int16_t") + { + int16_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int32_t") + { + int32_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int64_t") + { + int64_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_fast8_t") + { + int_fast8_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_fast16_t") + { + int_fast16_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_fast32_t") + { + int_fast32_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_fast64_t") + { + int_fast64_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_least8_t") + { + int_least8_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_least16_t") + { + int_least16_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_least32_t") + { + int_least32_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_least64_t") + { + int_least64_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("uint8_t") + { + uint8_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint16_t") + { + uint16_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint32_t") + { + uint32_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint64_t") + { + uint64_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_fast8_t") + { + uint_fast8_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_fast16_t") + { + uint_fast16_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_fast32_t") + { + uint_fast32_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_fast64_t") + { + uint_fast64_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_least8_t") + { + uint_least8_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_least16_t") + { + uint_least16_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_least32_t") + { + uint_least32_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_least64_t") + { + uint_least64_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("integer literal without suffix") + { + json j(42); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("integer literal with u suffix") + { + json j(42u); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("integer literal with l suffix") + { + json j(42l); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("integer literal with ul suffix") + { + json j(42ul); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("integer literal with ll suffix") + { + json j(42ll); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("integer literal with ull suffix") + { + json j(42ull); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + } + + SECTION("create a floating-point number (explicit)") + { + SECTION("uninitialized value") + { + json::number_float_t n{}; + json j(n); + CHECK(j.type() == json::value_t::number_float); + } + + SECTION("initialized value") + { + json::number_float_t n(42.23); + json j(n); + CHECK(j.type() == json::value_t::number_float); + } + } + + SECTION("create a floating-point number (implicit)") + { + // reference object + json::number_float_t n_reference = 42.23; + json j_reference(n_reference); + + SECTION("float") + { + float n = 42.23; + json j(n); + CHECK(j.type() == json::value_t::number_float); + CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); + } + + SECTION("double") + { + double n = 42.23; + json j(n); + CHECK(j.type() == json::value_t::number_float); + CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); + } + + SECTION("long double") + { + long double n = 42.23; + json j(n); + CHECK(j.type() == json::value_t::number_float); + CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); + } + + SECTION("floating-point literal without suffix") + { + json j(42.23); + CHECK(j.type() == json::value_t::number_float); + CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); + } + + SECTION("integer literal with f suffix") + { + json j(42.23f); + CHECK(j.type() == json::value_t::number_float); + CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); + } + + SECTION("integer literal with l suffix") + { + json j(42.23l); + CHECK(j.type() == json::value_t::number_float); + CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); + } + } + + SECTION("create a container (array or object) from an initializer list") + { + SECTION("empty initializer list") + { + SECTION("explicit") + { + std::initializer_list l; + json j(l); + CHECK(j.type() == json::value_t::object); + } + + SECTION("implicit") + { + json j {}; + CHECK(j.type() == json::value_t::null); + } + } + + SECTION("one element") + { + SECTION("array") + { + SECTION("explicit") + { + std::initializer_list l = {json(json::array_t())}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {json::array_t()}; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("object") + { + SECTION("explicit") + { + std::initializer_list l = {json(json::object_t())}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {json::object_t()}; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("string") + { + SECTION("explicit") + { + std::initializer_list l = {json("Hello world")}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {"Hello world"}; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("boolean") + { + SECTION("explicit") + { + std::initializer_list l = {json(true)}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {true}; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("number (integer)") + { + SECTION("explicit") + { + std::initializer_list l = {json(1)}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {1}; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("number (unsigned)") + { + SECTION("explicit") + { + std::initializer_list l = {json(1u)}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {1u}; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("number (floating-point)") + { + SECTION("explicit") + { + std::initializer_list l = {json(42.23)}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {42.23}; + CHECK(j.type() == json::value_t::array); + } + } + } + + SECTION("more elements") + { + SECTION("explicit") + { + std::initializer_list l = {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()}; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("implicit type deduction") + { + SECTION("object") + { + json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }; + CHECK(j.type() == json::value_t::object); + } + + SECTION("array") + { + json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} , 13 }; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("explicit type deduction") + { + SECTION("empty object") + { + json j = json::object(); + CHECK(j.type() == json::value_t::object); + } + + SECTION("object") + { + json j = json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }); + CHECK(j.type() == json::value_t::object); + } + + SECTION("object with error") + { + CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), + std::logic_error); + CHECK_THROWS_WITH(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), + "cannot create object from initializer list"); + } + + SECTION("empty array") + { + json j = json::array(); + CHECK(j.type() == json::value_t::array); + } + + SECTION("array") + { + json j = json::array({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }); + CHECK(j.type() == json::value_t::array); + } + } + } + + SECTION("create an array of n copies of a given value") + { + json v = {1, "foo", 34.23, {1, 2, 3}, {{"A", 1}, {"B", 2u}}}; + json arr(3, v); + CHECK(arr.size() == 3); + for (auto& x : arr) + { + CHECK(x == v); + } + } + + SECTION("create a JSON container from an iterator range") + { + SECTION("object") + { + SECTION("json(begin(), end())") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json j_new(jobject.begin(), jobject.end()); + CHECK(j_new == jobject); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json j_new(jobject.cbegin(), jobject.cend()); + CHECK(j_new == jobject); + } + } + + SECTION("json(begin(), begin())") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json j_new(jobject.begin(), jobject.begin()); + CHECK(j_new == json::object()); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json j_new(jobject.cbegin(), jobject.cbegin()); + CHECK(j_new == json::object()); + } + } + + SECTION("construct from subrange") + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json j_new(jobject.find("b"), jobject.find("e")); + CHECK(j_new == json({{"b", 1}, {"c", 17u}, {"d", false}})); + } + + SECTION("incompatible iterators") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), std::domain_error); + CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), std::domain_error); + CHECK_THROWS_WITH(json(jobject.begin(), jobject2.end()), "iterators are not compatible"); + CHECK_THROWS_WITH(json(jobject2.begin(), jobject.end()), "iterators are not compatible"); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), std::domain_error); + CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), std::domain_error); + CHECK_THROWS_WITH(json(jobject.cbegin(), jobject2.cend()), "iterators are not compatible"); + CHECK_THROWS_WITH(json(jobject2.cbegin(), jobject.cend()), "iterators are not compatible"); + } + } + } + + SECTION("array") + { + SECTION("json(begin(), end())") + { + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.begin(), jarray.end()); + CHECK(j_new == jarray); + } + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.cbegin(), jarray.cend()); + CHECK(j_new == jarray); + } + } + + SECTION("json(begin(), begin())") + { + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.begin(), jarray.begin()); + CHECK(j_new == json::array()); + } + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.cbegin(), jarray.cbegin()); + CHECK(j_new == json::array()); + } + } + + SECTION("construct from subrange") + { + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.begin() + 1, jarray.begin() + 3); + CHECK(j_new == json({2, 3})); + } + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.cbegin() + 1, jarray.cbegin() + 3); + CHECK(j_new == json({2, 3})); + } + } + + SECTION("incompatible iterators") + { + { + json jarray = {1, 2, 3, 4}; + json jarray2 = {2, 3, 4, 5}; + CHECK_THROWS_AS(json(jarray.begin(), jarray2.end()), std::domain_error); + CHECK_THROWS_AS(json(jarray2.begin(), jarray.end()), std::domain_error); + CHECK_THROWS_WITH(json(jarray.begin(), jarray2.end()), "iterators are not compatible"); + CHECK_THROWS_WITH(json(jarray2.begin(), jarray.end()), "iterators are not compatible"); + } + { + json jarray = {1, 2, 3, 4}; + json jarray2 = {2, 3, 4, 5}; + CHECK_THROWS_AS(json(jarray.cbegin(), jarray2.cend()), std::domain_error); + CHECK_THROWS_AS(json(jarray2.cbegin(), jarray.cend()), std::domain_error); + CHECK_THROWS_WITH(json(jarray.cbegin(), jarray2.cend()), "iterators are not compatible"); + CHECK_THROWS_WITH(json(jarray2.cbegin(), jarray.cend()), "iterators are not compatible"); + } + } + } + + SECTION("other values") + { + SECTION("construct with two valid iterators") + { + SECTION("null") + { + { + json j; + CHECK_THROWS_AS(json(j.begin(), j.end()), std::domain_error); + CHECK_THROWS_WITH(json(j.begin(), j.end()), "cannot use construct with iterators from null"); + } + { + json j; + CHECK_THROWS_AS(json(j.cbegin(), j.cend()), std::domain_error); + CHECK_THROWS_WITH(json(j.cbegin(), j.cend()), "cannot use construct with iterators from null"); + } + } + + SECTION("string") + { + { + json j = "foo"; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = "bar"; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = true; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = 17; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = 17u; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = 23.42; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + } + + SECTION("construct with two invalid iterators") + { + SECTION("string") + { + { + json j = "foo"; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = "bar"; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = true; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 17; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (integer)") + { + { + json j = 17u; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 17u; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 23.42; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + } + } + } + + SECTION("create a JSON value from an input stream") + { + SECTION("std::stringstream") + { + std::stringstream ss; + ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; + json j(ss); + CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); + } + + SECTION("with callback function") + { + std::stringstream ss; + ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; + json j(ss, [](int, json::parse_event_t, const json & val) + { + // filter all number(2) elements + if (val == json(2)) + { + return false; + } + else + { + return true; + } + }); + CHECK(j == json({"foo", 1, 3, false, {{"one", 1}}})); + } + + SECTION("std::ifstream") + { + std::ifstream f("test/data/json_tests/pass1.json"); + json j(f); + } + } +} + +TEST_CASE("other constructors and destructor") +{ + SECTION("copy constructor") + { + SECTION("object") + { + json j {{"foo", 1}, {"bar", false}}; + json k(j); + CHECK(j == k); + } + + SECTION("array") + { + json j {"foo", 1, 42.23, false}; + json k(j); + CHECK(j == k); + } + + SECTION("null") + { + json j(nullptr); + json k(j); + CHECK(j == k); + } + + SECTION("boolean") + { + json j(true); + json k(j); + CHECK(j == k); + } + + SECTION("string") + { + json j("Hello world"); + json k(j); + CHECK(j == k); + } + + SECTION("number (integer)") + { + json j(42); + json k(j); + CHECK(j == k); + } + + SECTION("number (unsigned)") + { + json j(42u); + json k(j); + CHECK(j == k); + } + + SECTION("number (floating-point)") + { + json j(42.23); + json k(j); + CHECK(j == k); + } + } + + SECTION("move constructor") + { + json j {{"foo", "bar"}, {"baz", {1, 2, 3, 4}}, {"a", 42u}, {"b", 42.23}, {"c", nullptr}}; + CHECK(j.type() == json::value_t::object); + json k(std::move(j)); + CHECK(k.type() == json::value_t::object); + CHECK(j.type() == json::value_t::null); + } + + SECTION("copy assignment") + { + SECTION("object") + { + json j {{"foo", 1}, {"bar", false}}; + json k; + k = j; + CHECK(j == k); + } + + SECTION("array") + { + json j {"foo", 1, 42.23, false}; + json k; + k = j; + CHECK(j == k); + } + + SECTION("null") + { + json j(nullptr); + json k; + k = j; + CHECK(j == k); + } + + SECTION("boolean") + { + json j(true); + json k; + k = j; + CHECK(j == k); + } + + SECTION("string") + { + json j("Hello world"); + json k; + k = j; + CHECK(j == k); + } + + SECTION("number (integer)") + { + json j(42); + json k; + k = j; + CHECK(j == k); + } + + SECTION("number (unsigned)") + { + json j(42u); + json k; + k = j; + CHECK(j == k); + } + + SECTION("number (floating-point)") + { + json j(42.23); + json k; + k = j; + CHECK(j == k); + } + } + + SECTION("destructor") + { + SECTION("object") + { + auto j = new json {{"foo", 1}, {"bar", false}}; + delete j; + } + + SECTION("array") + { + auto j = new json {"foo", 1, 1u, false, 23.42}; + delete j; + } + + SECTION("string") + { + auto j = new json("Hello world"); + delete j; + } + } +} diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp new file mode 100644 index 00000000..4d3c9b7c --- /dev/null +++ b/test/src/unit-convenience.cpp @@ -0,0 +1,92 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("convenience functions") +{ + SECTION("type name as string") + { + CHECK(json(json::value_t::null).type_name() == "null"); + CHECK(json(json::value_t::object).type_name() == "object"); + CHECK(json(json::value_t::array).type_name() == "array"); + CHECK(json(json::value_t::number_integer).type_name() == "number"); + CHECK(json(json::value_t::number_unsigned).type_name() == "number"); + CHECK(json(json::value_t::number_float).type_name() == "number"); + CHECK(json(json::value_t::boolean).type_name() == "boolean"); + CHECK(json(json::value_t::string).type_name() == "string"); + CHECK(json(json::value_t::discarded).type_name() == "discarded"); + } + + SECTION("string escape") + { + CHECK(json::escape_string("\"") == "\\\""); + CHECK(json::escape_string("\\") == "\\\\"); + CHECK(json::escape_string("\b") == "\\b"); + CHECK(json::escape_string("\f") == "\\f"); + CHECK(json::escape_string("\n") == "\\n"); + CHECK(json::escape_string("\r") == "\\r"); + CHECK(json::escape_string("\t") == "\\t"); + + CHECK(json::escape_string("\x01") == "\\u0001"); + CHECK(json::escape_string("\x02") == "\\u0002"); + CHECK(json::escape_string("\x03") == "\\u0003"); + CHECK(json::escape_string("\x04") == "\\u0004"); + CHECK(json::escape_string("\x05") == "\\u0005"); + CHECK(json::escape_string("\x06") == "\\u0006"); + CHECK(json::escape_string("\x07") == "\\u0007"); + CHECK(json::escape_string("\x08") == "\\b"); + CHECK(json::escape_string("\x09") == "\\t"); + CHECK(json::escape_string("\x0a") == "\\n"); + CHECK(json::escape_string("\x0b") == "\\u000b"); + CHECK(json::escape_string("\x0c") == "\\f"); + CHECK(json::escape_string("\x0d") == "\\r"); + CHECK(json::escape_string("\x0e") == "\\u000e"); + CHECK(json::escape_string("\x0f") == "\\u000f"); + CHECK(json::escape_string("\x10") == "\\u0010"); + CHECK(json::escape_string("\x11") == "\\u0011"); + CHECK(json::escape_string("\x12") == "\\u0012"); + CHECK(json::escape_string("\x13") == "\\u0013"); + CHECK(json::escape_string("\x14") == "\\u0014"); + CHECK(json::escape_string("\x15") == "\\u0015"); + CHECK(json::escape_string("\x16") == "\\u0016"); + CHECK(json::escape_string("\x17") == "\\u0017"); + CHECK(json::escape_string("\x18") == "\\u0018"); + CHECK(json::escape_string("\x19") == "\\u0019"); + CHECK(json::escape_string("\x1a") == "\\u001a"); + CHECK(json::escape_string("\x1b") == "\\u001b"); + CHECK(json::escape_string("\x1c") == "\\u001c"); + CHECK(json::escape_string("\x1d") == "\\u001d"); + CHECK(json::escape_string("\x1e") == "\\u001e"); + CHECK(json::escape_string("\x1f") == "\\u001f"); + } +} diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp new file mode 100644 index 00000000..982efe42 --- /dev/null +++ b/test/src/unit-conversions.cpp @@ -0,0 +1,1014 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +#include +#include +#include +#include +#include + +TEST_CASE("value conversion") +{ + SECTION("get an object (explicit)") + { + json::object_t o_reference = {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; + json j(o_reference); + + SECTION("json::object_t") + { + json::object_t o = j.get(); + CHECK(json(o) == j); + } + + SECTION("std::map") + { + std::map o = j.get>(); + CHECK(json(o) == j); + } + + SECTION("std::multimap") + { + std::multimap o = j.get>(); + CHECK(json(o) == j); + } + + SECTION("std::unordered_map") + { + std::unordered_map o = j.get>(); + CHECK(json(o) == j); + } + + SECTION("std::unordered_multimap") + { + std::unordered_multimap o = + j.get>(); + CHECK(json(o) == j); + } + + SECTION("exception in case of a non-object type") + { + CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); + + CHECK_THROWS_WITH(json(json::value_t::null).get(), + "type must be object, but is null"); + CHECK_THROWS_WITH(json(json::value_t::array).get(), + "type must be object, but is array"); + CHECK_THROWS_WITH(json(json::value_t::string).get(), + "type must be object, but is string"); + CHECK_THROWS_WITH(json(json::value_t::boolean).get(), + "type must be object, but is boolean"); + CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), + "type must be object, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), + "type must be object, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_float).get(), + "type must be object, but is number"); + } + } + + SECTION("get an object (implicit)") + { + json::object_t o_reference = {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; + json j(o_reference); + + SECTION("json::object_t") + { + json::object_t o = j; + CHECK(json(o) == j); + } + + SECTION("std::map") + { + std::map o = j; + CHECK(json(o) == j); + } + + SECTION("std::multimap") + { + std::multimap o = j; + CHECK(json(o) == j); + } + + SECTION("std::unordered_map") + { + std::unordered_map o = j; + CHECK(json(o) == j); + } + + SECTION("std::unordered_multimap") + { + std::unordered_multimap o = j; + CHECK(json(o) == j); + } + } + + SECTION("get an array (explicit)") + { + json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a_reference); + + SECTION("json::array_t") + { + json::array_t a = j.get(); + CHECK(json(a) == j); + } + + SECTION("std::list") + { + std::list a = j.get>(); + CHECK(json(a) == j); + } + + SECTION("std::forward_list") + { + std::forward_list a = j.get>(); + CHECK(json(a) == j); + } + + SECTION("std::vector") + { + std::vector a = j.get>(); + CHECK(json(a) == j); + } + + SECTION("std::deque") + { + std::deque a = j.get>(); + CHECK(json(a) == j); + } + + SECTION("exception in case of a non-array type") + { + CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); + + CHECK_THROWS_WITH(json(json::value_t::null).get(), + "type must be array, but is null"); + CHECK_THROWS_WITH(json(json::value_t::object).get(), + "type must be array, but is object"); + CHECK_THROWS_WITH(json(json::value_t::string).get(), + "type must be array, but is string"); + CHECK_THROWS_WITH(json(json::value_t::boolean).get(), + "type must be array, but is boolean"); + CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), + "type must be array, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), + "type must be array, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_float).get(), + "type must be array, but is number"); + } + } + + SECTION("get an array (implicit)") + { + json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a_reference); + + SECTION("json::array_t") + { + json::array_t a = j; + CHECK(json(a) == j); + } + + SECTION("std::list") + { + std::list a = j; + CHECK(json(a) == j); + } + + SECTION("std::forward_list") + { + std::forward_list a = j; + CHECK(json(a) == j); + } + + SECTION("std::vector") + { + std::vector a = j; + CHECK(json(a) == j); + } + + SECTION("std::deque") + { + std::deque a = j; + CHECK(json(a) == j); + } + } + + SECTION("get a string (explicit)") + { + json::string_t s_reference {"Hello world"}; + json j(s_reference); + + SECTION("string_t") + { + json::string_t s = j.get(); + CHECK(json(s) == j); + } + + SECTION("std::string") + { + std::string s = j.get(); + CHECK(json(s) == j); + } + + SECTION("exception in case of a non-string type") + { + CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); + + CHECK_THROWS_WITH(json(json::value_t::null).get(), + "type must be string, but is null"); + CHECK_THROWS_WITH(json(json::value_t::object).get(), + "type must be string, but is object"); + CHECK_THROWS_WITH(json(json::value_t::array).get(), + "type must be string, but is array"); + CHECK_THROWS_WITH(json(json::value_t::boolean).get(), + "type must be string, but is boolean"); + CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), + "type must be string, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), + "type must be string, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_float).get(), + "type must be string, but is number"); + } + } + + SECTION("get a string (implicit)") + { + json::string_t s_reference {"Hello world"}; + json j(s_reference); + + SECTION("string_t") + { + json::string_t s = j; + CHECK(json(s) == j); + } + + SECTION("std::string") + { + std::string s = j; + CHECK(json(s) == j); + } + } + + SECTION("get a boolean (explicit)") + { + json::boolean_t b_reference {true}; + json j(b_reference); + + SECTION("boolean_t") + { + json::boolean_t b = j.get(); + CHECK(json(b) == j); + } + + SECTION("bool") + { + bool b = j.get(); + CHECK(json(b) == j); + } + + SECTION("exception in case of a non-string type") + { + CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); + + CHECK_THROWS_WITH(json(json::value_t::null).get(), + "type must be boolean, but is null"); + CHECK_THROWS_WITH(json(json::value_t::object).get(), + "type must be boolean, but is object"); + CHECK_THROWS_WITH(json(json::value_t::array).get(), + "type must be boolean, but is array"); + CHECK_THROWS_WITH(json(json::value_t::string).get(), + "type must be boolean, but is string"); + CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), + "type must be boolean, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), + "type must be boolean, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_float).get(), + "type must be boolean, but is number"); + } + } + + SECTION("get a boolean (implicit)") + { + json::boolean_t b_reference {true}; + json j(b_reference); + + SECTION("boolean_t") + { + json::boolean_t b = j; + CHECK(json(b) == j); + } + + SECTION("bool") + { + bool b = j; + CHECK(json(b) == j); + } + } + + SECTION("get an integer number (explicit)") + { + json::number_integer_t n_reference {42}; + json j(n_reference); + json::number_unsigned_t n_unsigned_reference {42u}; + json j_unsigned(n_unsigned_reference); + + SECTION("number_integer_t") + { + json::number_integer_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("number_unsigned_t") + { + json::number_unsigned_t n = j_unsigned.get(); + CHECK(json(n) == j_unsigned); + } + + SECTION("short") + { + short n = j.get(); + CHECK(json(n) == j); + } + + SECTION("unsigned short") + { + unsigned short n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int") + { + int n = j.get(); + CHECK(json(n) == j); + } + + SECTION("unsigned int") + { + unsigned int n = j.get(); + CHECK(json(n) == j); + } + + SECTION("long") + { + long n = j.get(); + CHECK(json(n) == j); + } + + SECTION("unsigned long") + { + unsigned long n = j.get(); + CHECK(json(n) == j); + } + + SECTION("long long") + { + long long n = j.get(); + CHECK(json(n) == j); + } + + SECTION("unsigned long long") + { + unsigned long long n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int8_t") + { + int8_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int16_t") + { + int16_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int32_t") + { + int32_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int64_t") + { + int64_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int8_fast_t") + { + int_fast8_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int16_fast_t") + { + int_fast16_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int32_fast_t") + { + int_fast32_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int64_fast_t") + { + int_fast64_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int8_least_t") + { + int_least8_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int16_least_t") + { + int_least16_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int32_least_t") + { + int_least32_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int64_least_t") + { + int_least64_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint8_t") + { + uint8_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint16_t") + { + uint16_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint32_t") + { + uint32_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint64_t") + { + uint64_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint8_fast_t") + { + uint_fast8_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint16_fast_t") + { + uint_fast16_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint32_fast_t") + { + uint_fast32_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint64_fast_t") + { + uint_fast64_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint8_least_t") + { + uint_least8_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint16_least_t") + { + uint_least16_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint32_least_t") + { + uint_least32_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint64_least_t") + { + uint_least64_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("exception in case of a non-number type") + { + CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); + + CHECK_THROWS_WITH(json(json::value_t::null).get(), + "type must be number, but is null"); + CHECK_THROWS_WITH(json(json::value_t::object).get(), + "type must be number, but is object"); + CHECK_THROWS_WITH(json(json::value_t::array).get(), + "type must be number, but is array"); + CHECK_THROWS_WITH(json(json::value_t::string).get(), + "type must be number, but is string"); + CHECK_THROWS_WITH(json(json::value_t::boolean).get(), + "type must be number, but is boolean"); + + CHECK_NOTHROW(json(json::value_t::number_float).get()); + CHECK_NOTHROW(json(json::value_t::number_float).get()); + } + } + + SECTION("get an integer number (implicit)") + { + json::number_integer_t n_reference {42}; + json j(n_reference); + json::number_unsigned_t n_unsigned_reference {42u}; + json j_unsigned(n_unsigned_reference); + + SECTION("number_integer_t") + { + json::number_integer_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("number_unsigned_t") + { + json::number_unsigned_t n = j_unsigned.get(); + CHECK(json(n) == j_unsigned); + } + + SECTION("short") + { + short n = j; + CHECK(json(n) == j); + } + + SECTION("unsigned short") + { + unsigned short n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("int") + { + int n = j; + CHECK(json(n) == j); + } + + SECTION("unsigned int") + { + unsigned int n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("long") + { + long n = j; + CHECK(json(n) == j); + } + + SECTION("unsigned long") + { + unsigned long n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("long long") + { + long long n = j; + CHECK(json(n) == j); + } + + SECTION("unsigned long long") + { + unsigned long long n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("int8_t") + { + int8_t n = j; + CHECK(json(n) == j); + } + + SECTION("int16_t") + { + int16_t n = j; + CHECK(json(n) == j); + } + + SECTION("int32_t") + { + int32_t n = j; + CHECK(json(n) == j); + } + + SECTION("int64_t") + { + int64_t n = j; + CHECK(json(n) == j); + } + + SECTION("int8_fast_t") + { + int_fast8_t n = j; + CHECK(json(n) == j); + } + + SECTION("int16_fast_t") + { + int_fast16_t n = j; + CHECK(json(n) == j); + } + + SECTION("int32_fast_t") + { + int_fast32_t n = j; + CHECK(json(n) == j); + } + + SECTION("int64_fast_t") + { + int_fast64_t n = j; + CHECK(json(n) == j); + } + + SECTION("int8_least_t") + { + int_least8_t n = j; + CHECK(json(n) == j); + } + + SECTION("int16_least_t") + { + int_least16_t n = j; + CHECK(json(n) == j); + } + + SECTION("int32_least_t") + { + int_least32_t n = j; + CHECK(json(n) == j); + } + + SECTION("int64_least_t") + { + int_least64_t n = j; + CHECK(json(n) == j); + } + + SECTION("uint8_t") + { + uint8_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint16_t") + { + uint16_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint32_t") + { + uint32_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint64_t") + { + uint64_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint8_fast_t") + { + uint_fast8_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint16_fast_t") + { + uint_fast16_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint32_fast_t") + { + uint_fast32_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint64_fast_t") + { + uint_fast64_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint8_least_t") + { + uint_least8_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint16_least_t") + { + uint_least16_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint32_least_t") + { + uint_least32_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint64_least_t") + { + uint_least64_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + } + + SECTION("get a floating-point number (explicit)") + { + json::number_float_t n_reference {42.23}; + json j(n_reference); + + SECTION("number_float_t") + { + json::number_float_t n = j.get(); + CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); + } + + SECTION("float") + { + float n = j.get(); + CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); + } + + SECTION("double") + { + double n = j.get(); + CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); + } + + SECTION("exception in case of a non-string type") + { + CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); + + CHECK_THROWS_WITH(json(json::value_t::null).get(), + "type must be number, but is null"); + CHECK_THROWS_WITH(json(json::value_t::object).get(), + "type must be number, but is object"); + CHECK_THROWS_WITH(json(json::value_t::array).get(), + "type must be number, but is array"); + CHECK_THROWS_WITH(json(json::value_t::string).get(), + "type must be number, but is string"); + CHECK_THROWS_WITH(json(json::value_t::boolean).get(), + "type must be number, but is boolean"); + + CHECK_NOTHROW(json(json::value_t::number_integer).get()); + CHECK_NOTHROW(json(json::value_t::number_unsigned).get()); + } + } + + SECTION("get a floating-point number (implicit)") + { + json::number_float_t n_reference {42.23}; + json j(n_reference); + + SECTION("number_float_t") + { + json::number_float_t n = j; + CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); + } + + SECTION("float") + { + float n = j; + CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); + } + + SECTION("double") + { + double n = j; + CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); + } + } + + SECTION("more involved conversions") + { + SECTION("object-like STL containers") + { + json j1 = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j2 = {{"one", 1u}, {"two", 2u}, {"three", 3u}}; + json j3 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}; + json j4 = {{"one", true}, {"two", false}, {"three", true}}; + json j5 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}; + + SECTION("std::map") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + //auto m5 = j5.get>(); + } + + SECTION("std::unordered_map") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + //auto m5 = j5.get>(); + //CHECK(m5["one"] == "eins"); + } + + SECTION("std::multimap") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + //auto m5 = j5.get>(); + //CHECK(m5["one"] == "eins"); + } + + SECTION("std::unordered_multimap") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + //auto m5 = j5.get>(); + //CHECK(m5["one"] == "eins"); + } + + SECTION("exception in case of a non-object type") + { + CHECK_THROWS_AS((json().get>()), std::logic_error); + CHECK_THROWS_WITH((json().get>()), "type must be object, but is null"); + } + } + + SECTION("array-like STL containers") + { + json j1 = {1, 2, 3, 4}; + json j2 = {1u, 2u, 3u, 4u}; + json j3 = {1.2, 2.3, 3.4, 4.5}; + json j4 = {true, false, true}; + json j5 = {"one", "two", "three"}; + + SECTION("std::list") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); + } + + //SECTION("std::forward_list") + //{ + // auto m1 = j1.get>(); + // auto m2 = j2.get>(); + // auto m3 = j3.get>(); + // auto m4 = j4.get>(); + // auto m5 = j5.get>(); + //} + + SECTION("std::vector") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); + } + + SECTION("std::deque") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j2.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); + } + + SECTION("std::set") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); + } + + SECTION("std::unordered_set") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); + } + + SECTION("exception in case of a non-object type") + { + CHECK_THROWS_AS((json().get>()), std::logic_error); + CHECK_THROWS_AS((json().get>()), std::logic_error); + CHECK_THROWS_AS((json().get>()), std::logic_error); + CHECK_THROWS_AS((json().get>()), std::logic_error); + + CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); + CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); + CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); + CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); + } + } + } +} diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp new file mode 100644 index 00000000..78188574 --- /dev/null +++ b/test/src/unit-deserialization.cpp @@ -0,0 +1,73 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("deserialization") +{ + SECTION("stream") + { + std::stringstream ss; + ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; + json j = json::parse(ss); + CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); + } + + SECTION("string") + { + auto s = "[\"foo\",1,2,3,false,{\"one\":1}]"; + json j = json::parse(s); + CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); + } + + SECTION("operator<<") + { + std::stringstream ss; + ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; + json j; + j << ss; + CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); + } + + SECTION("operator>>") + { + std::stringstream ss; + ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; + json j; + ss >> j; + CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); + } + + SECTION("user-defined string literal") + { + CHECK("[\"foo\",1,2,3,false,{\"one\":1}]"_json == json({"foo", 1, 2, 3, false, {{"one", 1}}})); + } +} diff --git a/test/src/unit-element_access.cpp b/test/src/unit-element_access.cpp new file mode 100644 index 00000000..bd33e1e0 --- /dev/null +++ b/test/src/unit-element_access.cpp @@ -0,0 +1,1871 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("element access") +{ + SECTION("array") + { + json j = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + const json j_const = j; + + SECTION("access specified element with bounds checking") + { + SECTION("access within bounds") + { + CHECK(j.at(0) == json(1)); + CHECK(j.at(1) == json(1u)); + CHECK(j.at(2) == json(true)); + CHECK(j.at(3) == json(nullptr)); + CHECK(j.at(4) == json("string")); + CHECK(j.at(5) == json(42.23)); + CHECK(j.at(6) == json(json::object())); + CHECK(j.at(7) == json({1, 2, 3})); + + CHECK(j_const.at(0) == json(1)); + CHECK(j_const.at(1) == json(1u)); + CHECK(j_const.at(2) == json(true)); + CHECK(j_const.at(3) == json(nullptr)); + CHECK(j_const.at(4) == json("string")); + CHECK(j_const.at(5) == json(42.23)); + CHECK(j_const.at(6) == json(json::object())); + CHECK(j_const.at(7) == json({1, 2, 3})); + } + + SECTION("access outside bounds") + { + CHECK_THROWS_AS(j.at(8), std::out_of_range); + CHECK_THROWS_AS(j_const.at(8), std::out_of_range); + + CHECK_THROWS_WITH(j.at(8), "array index 8 is out of range"); + CHECK_THROWS_WITH(j_const.at(8), "array index 8 is out of range"); + } + + SECTION("access on non-array type") + { + SECTION("null") + { + json j_nonarray(json::value_t::null); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with null"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with null"); + } + + SECTION("boolean") + { + json j_nonarray(json::value_t::boolean); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with boolean"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with boolean"); + } + + SECTION("string") + { + json j_nonarray(json::value_t::string); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with string"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with string"); + } + + SECTION("object") + { + json j_nonarray(json::value_t::object); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with object"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with object"); + } + + SECTION("number (integer)") + { + json j_nonarray(json::value_t::number_integer); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); + } + + SECTION("number (unsigned)") + { + json j_nonarray(json::value_t::number_unsigned); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); + } + + SECTION("number (floating-point)") + { + json j_nonarray(json::value_t::number_float); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); + } + } + } + + SECTION("front and back") + { + CHECK(j.front() == json(1)); + CHECK(j_const.front() == json(1)); + CHECK(j.back() == json({1, 2, 3})); + CHECK(j_const.back() == json({1, 2, 3})); + } + + SECTION("access specified element") + { + SECTION("access within bounds") + { + CHECK(j[0] == json(1)); + CHECK(j[1] == json(1u)); + CHECK(j[2] == json(true)); + CHECK(j[3] == json(nullptr)); + CHECK(j[4] == json("string")); + CHECK(j[5] == json(42.23)); + CHECK(j[6] == json(json::object())); + CHECK(j[7] == json({1, 2, 3})); + + CHECK(j_const[0] == json(1)); + CHECK(j_const[1] == json(1u)); + CHECK(j_const[2] == json(true)); + CHECK(j_const[3] == json(nullptr)); + CHECK(j_const[4] == json("string")); + CHECK(j_const[5] == json(42.23)); + CHECK(j_const[6] == json(json::object())); + CHECK(j_const[7] == json({1, 2, 3})); + } + + SECTION("access on non-array type") + { + SECTION("null") + { + SECTION("standard tests") + { + json j_nonarray(json::value_t::null); + const json j_nonarray_const(j_nonarray); + CHECK_NOTHROW(j_nonarray[0]); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with null"); + } + + SECTION("implicit transformation to properly filled array") + { + json j_nonarray; + j_nonarray[3] = 42; + CHECK(j_nonarray == json({nullptr, nullptr, nullptr, 42})); + } + } + + SECTION("boolean") + { + json j_nonarray(json::value_t::boolean); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with boolean"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with boolean"); + } + + SECTION("string") + { + json j_nonarray(json::value_t::string); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with string"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with string"); + } + + SECTION("object") + { + json j_nonarray(json::value_t::object); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with object"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with object"); + } + + SECTION("number (integer)") + { + json j_nonarray(json::value_t::number_integer); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); + } + + SECTION("number (unsigned)") + { + json j_nonarray(json::value_t::number_unsigned); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); + } + + SECTION("number (floating-point)") + { + json j_nonarray(json::value_t::number_float); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); + } + } + } + + SECTION("remove specified element") + { + SECTION("remove element by index") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(0); + CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(1); + CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(2); + CHECK(jarray == json({1, 1u, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(3); + CHECK(jarray == json({1, 1u, true, "string", 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(4); + CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(5); + CHECK(jarray == json({1, 1u, true, nullptr, "string", json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(6); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(7); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object()})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + CHECK_THROWS_AS(jarray.erase(8), std::out_of_range); + CHECK_THROWS_WITH(jarray.erase(8), "array index 8 is out of range"); + } + } + + SECTION("remove element by iterator") + { + SECTION("erase(begin())") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it2 = jarray.erase(jarray.begin()); + CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(1u)); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it2 = jarray.erase(jarray.cbegin()); + CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(1u)); + } + } + + SECTION("erase(begin(), end())") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it2 = jarray.erase(jarray.begin(), jarray.end()); + CHECK(jarray == json::array()); + CHECK(it2 == jarray.end()); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cend()); + CHECK(jarray == json::array()); + CHECK(it2 == jarray.cend()); + } + } + + SECTION("erase(begin(), begin())") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it2 = jarray.erase(jarray.begin(), jarray.begin()); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(1)); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cbegin()); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(1)); + } + } + + SECTION("erase at offset") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it = jarray.begin() + 4; + json::iterator it2 = jarray.erase(it); + CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(42.23)); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it = jarray.cbegin() + 4; + json::const_iterator it2 = jarray.erase(it); + CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(42.23)); + } + } + + SECTION("erase subrange") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it2 = jarray.erase(jarray.begin() + 3, jarray.begin() + 6); + CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); + CHECK(*it2 == json::object()); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it2 = jarray.erase(jarray.cbegin() + 3, jarray.cbegin() + 6); + CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); + CHECK(*it2 == json::object()); + } + } + + SECTION("different arrays") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray2 = {"foo", "bar"}; + CHECK_THROWS_AS(jarray.erase(jarray2.begin()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray.end()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray2.end()), std::domain_error); + + CHECK_THROWS_WITH(jarray.erase(jarray2.begin()), "iterator does not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray.begin(), jarray2.end()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray.end()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray2.end()), + "iterators do not fit current value"); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray2 = {"foo", "bar"}; + CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), std::domain_error); + + CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin()), "iterator does not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray.cbegin(), jarray2.cend()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray.cend()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray2.cend()), + "iterators do not fit current value"); + } + } + } + + SECTION("remove element by index in non-array type") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with null"); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with boolean"); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with string"); + } + + SECTION("object") + { + json j_nonobject(json::value_t::object); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with object"); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); + } + + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); + } + } + } + } + + SECTION("object") + { + json j = {{"integer", 1}, {"unsigned", 1u}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", json::object()}, {"array", {1, 2, 3}}}; + const json j_const = j; + + SECTION("access specified element with bounds checking") + { + SECTION("access within bounds") + { + CHECK(j.at("integer") == json(1)); + CHECK(j.at("unsigned") == json(1u)); + CHECK(j.at("boolean") == json(true)); + CHECK(j.at("null") == json(nullptr)); + CHECK(j.at("string") == json("hello world")); + CHECK(j.at("floating") == json(42.23)); + CHECK(j.at("object") == json(json::object())); + CHECK(j.at("array") == json({1, 2, 3})); + + CHECK(j_const.at("integer") == json(1)); + CHECK(j_const.at("unsigned") == json(1u)); + CHECK(j_const.at("boolean") == json(true)); + CHECK(j_const.at("null") == json(nullptr)); + CHECK(j_const.at("string") == json("hello world")); + CHECK(j_const.at("floating") == json(42.23)); + CHECK(j_const.at("object") == json(json::object())); + CHECK(j_const.at("array") == json({1, 2, 3})); + } + + SECTION("access outside bounds") + { + CHECK_THROWS_AS(j.at("foo"), std::out_of_range); + CHECK_THROWS_AS(j_const.at("foo"), std::out_of_range); + CHECK_THROWS_WITH(j.at("foo"), "key 'foo' not found"); + CHECK_THROWS_WITH(j_const.at("foo"), "key 'foo' not found"); + } + + SECTION("access on non-object type") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with null"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with null"); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with boolean"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with boolean"); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with string"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with string"); + } + + SECTION("array") + { + json j_nonobject(json::value_t::array); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with array"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with array"); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); + } + + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); + } + } + } + + SECTION("access specified element with default value") + { + SECTION("given a key") + { + SECTION("access existing value") + { + CHECK(j.value("integer", 2) == 1); + CHECK(j.value("integer", 1.0) == Approx(1)); + CHECK(j.value("unsigned", 2) == 1u); + CHECK(j.value("unsigned", 1.0) == Approx(1u)); + CHECK(j.value("null", json(1)) == json()); + CHECK(j.value("boolean", false) == true); + CHECK(j.value("string", "bar") == "hello world"); + CHECK(j.value("string", std::string("bar")) == "hello world"); + CHECK(j.value("floating", 12.34) == Approx(42.23)); + CHECK(j.value("floating", 12) == 42); + CHECK(j.value("object", json({{"foo", "bar"}})) == json(json::object())); + CHECK(j.value("array", json({10, 100})) == json({1, 2, 3})); + + CHECK(j_const.value("integer", 2) == 1); + CHECK(j_const.value("integer", 1.0) == Approx(1)); + CHECK(j_const.value("unsigned", 2) == 1u); + CHECK(j_const.value("unsigned", 1.0) == Approx(1u)); + CHECK(j_const.value("boolean", false) == true); + CHECK(j_const.value("string", "bar") == "hello world"); + CHECK(j_const.value("string", std::string("bar")) == "hello world"); + CHECK(j_const.value("floating", 12.34) == Approx(42.23)); + CHECK(j_const.value("floating", 12) == 42); + CHECK(j_const.value("object", json({{"foo", "bar"}})) == json(json::object())); + CHECK(j_const.value("array", json({10, 100})) == json({1, 2, 3})); + } + + SECTION("access non-existing value") + { + CHECK(j.value("_", 2) == 2); + CHECK(j.value("_", 2u) == 2u); + CHECK(j.value("_", false) == false); + CHECK(j.value("_", "bar") == "bar"); + CHECK(j.value("_", 12.34) == Approx(12.34)); + CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); + CHECK(j.value("_", json({10, 100})) == json({10, 100})); + + CHECK(j_const.value("_", 2) == 2); + CHECK(j_const.value("_", 2u) == 2u); + CHECK(j_const.value("_", false) == false); + CHECK(j_const.value("_", "bar") == "bar"); + CHECK(j_const.value("_", 12.34) == Approx(12.34)); + CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); + CHECK(j_const.value("_", json({10, 100})) == json({10, 100})); + } + + SECTION("access on non-object type") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with null"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with null"); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with boolean"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with boolean"); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with string"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with string"); + } + + SECTION("array") + { + json j_nonobject(json::value_t::array); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with array"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with array"); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); + } + + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); + } + } + } + + SECTION("given a JSON pointer") + { + SECTION("access existing value") + { + CHECK(j.value("/integer"_json_pointer, 2) == 1); + CHECK(j.value("/integer"_json_pointer, 1.0) == Approx(1)); + CHECK(j.value("/unsigned"_json_pointer, 2) == 1u); + CHECK(j.value("/unsigned"_json_pointer, 1.0) == Approx(1u)); + CHECK(j.value("/null"_json_pointer, json(1)) == json()); + CHECK(j.value("/boolean"_json_pointer, false) == true); + CHECK(j.value("/string"_json_pointer, "bar") == "hello world"); + CHECK(j.value("/string"_json_pointer, std::string("bar")) == "hello world"); + CHECK(j.value("/floating"_json_pointer, 12.34) == Approx(42.23)); + CHECK(j.value("/floating"_json_pointer, 12) == 42); + CHECK(j.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object())); + CHECK(j.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3})); + + CHECK(j_const.value("/integer"_json_pointer, 2) == 1); + CHECK(j_const.value("/integer"_json_pointer, 1.0) == Approx(1)); + CHECK(j_const.value("/unsigned"_json_pointer, 2) == 1u); + CHECK(j_const.value("/unsigned"_json_pointer, 1.0) == Approx(1u)); + CHECK(j_const.value("/boolean"_json_pointer, false) == true); + CHECK(j_const.value("/string"_json_pointer, "bar") == "hello world"); + CHECK(j_const.value("/string"_json_pointer, std::string("bar")) == "hello world"); + CHECK(j_const.value("/floating"_json_pointer, 12.34) == Approx(42.23)); + CHECK(j_const.value("/floating"_json_pointer, 12) == 42); + CHECK(j_const.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object())); + CHECK(j_const.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3})); + } + + SECTION("access non-existing value") + { + CHECK(j.value("/not/existing"_json_pointer, 2) == 2); + CHECK(j.value("/not/existing"_json_pointer, 2u) == 2u); + CHECK(j.value("/not/existing"_json_pointer, false) == false); + CHECK(j.value("/not/existing"_json_pointer, "bar") == "bar"); + CHECK(j.value("/not/existing"_json_pointer, 12.34) == Approx(12.34)); + CHECK(j.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}})); + CHECK(j.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100})); + + CHECK(j_const.value("/not/existing"_json_pointer, 2) == 2); + CHECK(j_const.value("/not/existing"_json_pointer, 2u) == 2u); + CHECK(j_const.value("/not/existing"_json_pointer, false) == false); + CHECK(j_const.value("/not/existing"_json_pointer, "bar") == "bar"); + CHECK(j_const.value("/not/existing"_json_pointer, 12.34) == Approx(12.34)); + CHECK(j_const.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}})); + CHECK(j_const.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100})); + } + + SECTION("access on non-object type") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with null"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with null"); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with boolean"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), + "cannot use value() with boolean"); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with string"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), + "cannot use value() with string"); + } + + SECTION("array") + { + json j_nonobject(json::value_t::array); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with array"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with array"); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), + "cannot use value() with number"); + } + + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), + "cannot use value() with number"); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), + "cannot use value() with number"); + } + } + } + } + + SECTION("front and back") + { + // "array" is the smallest key + CHECK(j.front() == json({1, 2, 3})); + CHECK(j_const.front() == json({1, 2, 3})); + // "unsigned" is the largest key + CHECK(j.back() == json(1u)); + CHECK(j_const.back() == json(1u)); + } + + SECTION("access specified element") + { + SECTION("access within bounds") + { + CHECK(j["integer"] == json(1)); + CHECK(j[json::object_t::key_type("integer")] == j["integer"]); + + CHECK(j["unsigned"] == json(1u)); + CHECK(j[json::object_t::key_type("unsigned")] == j["unsigned"]); + + CHECK(j["boolean"] == json(true)); + CHECK(j[json::object_t::key_type("boolean")] == j["boolean"]); + + CHECK(j["null"] == json(nullptr)); + CHECK(j[json::object_t::key_type("null")] == j["null"]); + + CHECK(j["string"] == json("hello world")); + CHECK(j[json::object_t::key_type("string")] == j["string"]); + + CHECK(j["floating"] == json(42.23)); + CHECK(j[json::object_t::key_type("floating")] == j["floating"]); + + CHECK(j["object"] == json(json::object())); + CHECK(j[json::object_t::key_type("object")] == j["object"]); + + CHECK(j["array"] == json({1, 2, 3})); + CHECK(j[json::object_t::key_type("array")] == j["array"]); + + CHECK(j_const["integer"] == json(1)); + CHECK(j_const[json::object_t::key_type("integer")] == j["integer"]); + + CHECK(j_const["boolean"] == json(true)); + CHECK(j_const[json::object_t::key_type("boolean")] == j["boolean"]); + + CHECK(j_const["null"] == json(nullptr)); + CHECK(j_const[json::object_t::key_type("null")] == j["null"]); + + CHECK(j_const["string"] == json("hello world")); + CHECK(j_const[json::object_t::key_type("string")] == j["string"]); + + CHECK(j_const["floating"] == json(42.23)); + CHECK(j_const[json::object_t::key_type("floating")] == j["floating"]); + + CHECK(j_const["object"] == json(json::object())); + CHECK(j_const[json::object_t::key_type("object")] == j["object"]); + + CHECK(j_const["array"] == json({1, 2, 3})); + CHECK(j_const[json::object_t::key_type("array")] == j["array"]); + } + + SECTION("access on non-object type") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + json j_nonobject2(json::value_t::null); + const json j_const_nonobject(j_nonobject); + CHECK_NOTHROW(j_nonobject["foo"]); + CHECK_NOTHROW(j_nonobject2[json::object_t::key_type("foo")]); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with null"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with null"); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with boolean"); + CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with boolean"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with boolean"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with boolean"); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with string"); + CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with string"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with string"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with string"); + } + + SECTION("array") + { + json j_nonobject(json::value_t::array); + const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with array"); + CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with array"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with array"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with array"); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with number"); + } + + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with number"); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with number"); + } + } + } + + SECTION("remove specified element") + { + SECTION("remove element by key") + { + CHECK(j.find("integer") != j.end()); + CHECK(j.erase("integer") == 1); + CHECK(j.find("integer") == j.end()); + CHECK(j.erase("integer") == 0); + + CHECK(j.find("unsigned") != j.end()); + CHECK(j.erase("unsigned") == 1); + CHECK(j.find("unsigned") == j.end()); + CHECK(j.erase("unsigned") == 0); + + CHECK(j.find("boolean") != j.end()); + CHECK(j.erase("boolean") == 1); + CHECK(j.find("boolean") == j.end()); + CHECK(j.erase("boolean") == 0); + + CHECK(j.find("null") != j.end()); + CHECK(j.erase("null") == 1); + CHECK(j.find("null") == j.end()); + CHECK(j.erase("null") == 0); + + CHECK(j.find("string") != j.end()); + CHECK(j.erase("string") == 1); + CHECK(j.find("string") == j.end()); + CHECK(j.erase("string") == 0); + + CHECK(j.find("floating") != j.end()); + CHECK(j.erase("floating") == 1); + CHECK(j.find("floating") == j.end()); + CHECK(j.erase("floating") == 0); + + CHECK(j.find("object") != j.end()); + CHECK(j.erase("object") == 1); + CHECK(j.find("object") == j.end()); + CHECK(j.erase("object") == 0); + + CHECK(j.find("array") != j.end()); + CHECK(j.erase("array") == 1); + CHECK(j.find("array") == j.end()); + CHECK(j.erase("array") == 0); + } + + SECTION("remove element by iterator") + { + SECTION("erase(begin())") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::iterator it2 = jobject.erase(jobject.begin()); + CHECK(jobject == json({{"b", 1}, {"c", 17u}})); + CHECK(*it2 == json(1)); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::const_iterator it2 = jobject.erase(jobject.cbegin()); + CHECK(jobject == json({{"b", 1}, {"c", 17u}})); + CHECK(*it2 == json(1)); + } + } + + SECTION("erase(begin(), end())") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::iterator it2 = jobject.erase(jobject.begin(), jobject.end()); + CHECK(jobject == json::object()); + CHECK(it2 == jobject.end()); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cend()); + CHECK(jobject == json::object()); + CHECK(it2 == jobject.cend()); + } + } + + SECTION("erase(begin(), begin())") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::iterator it2 = jobject.erase(jobject.begin(), jobject.begin()); + CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17u}})); + CHECK(*it2 == json("a")); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cbegin()); + CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17u}})); + CHECK(*it2 == json("a")); + } + } + + SECTION("erase at offset") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::iterator it = jobject.find("b"); + json::iterator it2 = jobject.erase(it); + CHECK(jobject == json({{"a", "a"}, {"c", 17u}})); + CHECK(*it2 == json(17)); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::const_iterator it = jobject.find("b"); + json::const_iterator it2 = jobject.erase(it); + CHECK(jobject == json({{"a", "a"}, {"c", 17u}})); + CHECK(*it2 == json(17)); + } + } + + SECTION("erase subrange") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json::iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e")); + CHECK(jobject == json({{"a", "a"}, {"e", true}})); + CHECK(*it2 == json(true)); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json::const_iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e")); + CHECK(jobject == json({{"a", "a"}, {"e", true}})); + CHECK(*it2 == json(true)); + } + } + + SECTION("different objects") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + CHECK_THROWS_AS(jobject.erase(jobject2.begin()), std::domain_error); + CHECK_THROWS_AS(jobject.erase(jobject.begin(), jobject2.end()), std::domain_error); + CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject.end()), std::domain_error); + CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject2.end()), std::domain_error); + CHECK_THROWS_WITH(jobject.erase(jobject2.begin()), "iterator does not fit current value"); + CHECK_THROWS_WITH(jobject.erase(jobject.begin(), jobject2.end()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject.end()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject2.end()), + "iterators do not fit current value"); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + CHECK_THROWS_AS(jobject.erase(jobject2.cbegin()), std::domain_error); + CHECK_THROWS_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), std::domain_error); + CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), std::domain_error); + CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject2.cend()), std::domain_error); + CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin()), "iterator does not fit current value"); + CHECK_THROWS_WITH(jobject.erase(jobject.cbegin(), jobject2.cend()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject.cend()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject2.cend()), + "iterators do not fit current value"); + } + } + } + + SECTION("remove element by key in non-object type") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with null"); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with boolean"); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with string"); + } + + SECTION("array") + { + json j_nonobject(json::value_t::array); + CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with array"); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number"); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number"); + } + } + } + + SECTION("find an element in an object") + { + SECTION("existing element") + { + for (auto key : + {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" + }) + { + CHECK(j.find(key) != j.end()); + CHECK(*j.find(key) == j.at(key)); + CHECK(j_const.find(key) != j_const.end()); + CHECK(*j_const.find(key) == j_const.at(key)); + } + } + + SECTION("nonexisting element") + { + CHECK(j.find("foo") == j.end()); + CHECK(j_const.find("foo") == j_const.end()); + } + + SECTION("all types") + { + SECTION("null") + { + json j_nonarray(json::value_t::null); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + + SECTION("string") + { + json j_nonarray(json::value_t::string); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + + SECTION("object") + { + json j_nonarray(json::value_t::object); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + + SECTION("array") + { + json j_nonarray(json::value_t::array); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + + SECTION("boolean") + { + json j_nonarray(json::value_t::boolean); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + + SECTION("number (integer)") + { + json j_nonarray(json::value_t::number_integer); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + + SECTION("number (unsigned)") + { + json j_nonarray(json::value_t::number_unsigned); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + + SECTION("number (floating-point)") + { + json j_nonarray(json::value_t::number_float); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + } + } + + SECTION("count keys in an object") + { + SECTION("existing element") + { + for (auto key : + {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" + }) + { + CHECK(j.count(key) == 1); + CHECK(j_const.count(key) == 1); + } + } + + SECTION("nonexisting element") + { + CHECK(j.count("foo") == 0); + CHECK(j_const.count("foo") == 0); + } + + SECTION("all types") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + + SECTION("object") + { + json j_nonobject(json::value_t::object); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + + SECTION("array") + { + json j_nonobject(json::value_t::array); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + } + } + } + + SECTION("other values") + { + SECTION("front and back") + { + SECTION("null") + { + { + json j; + CHECK_THROWS_AS(j.front(), std::out_of_range); + CHECK_THROWS_AS(j.back(), std::out_of_range); + CHECK_THROWS_WITH(j.front(), "cannot get value"); + CHECK_THROWS_WITH(j.back(), "cannot get value"); + } + { + const json j{}; + CHECK_THROWS_AS(j.front(), std::out_of_range); + CHECK_THROWS_AS(j.back(), std::out_of_range); + CHECK_THROWS_WITH(j.front(), "cannot get value"); + CHECK_THROWS_WITH(j.back(), "cannot get value"); + } + } + + SECTION("string") + { + { + json j = "foo"; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = "bar"; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = true; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = 17; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = 17u; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = 23.42; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + } + + SECTION("erase with one valid iterator") + { + SECTION("null") + { + { + json j; + CHECK_THROWS_AS(j.erase(j.begin()), std::domain_error); + CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); + } + { + json j; + CHECK_THROWS_AS(j.erase(j.cbegin()), std::domain_error); + CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); + } + } + + SECTION("string") + { + { + json j = "foo"; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = "bar"; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = true; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 17; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 17u; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 23.42; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + } + + SECTION("erase with one invalid iterator") + { + SECTION("string") + { + { + json j = "foo"; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = "bar"; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = true; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = 17; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = 23.42; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + } + + SECTION("erase with two valid iterators") + { + SECTION("null") + { + { + json j; + CHECK_THROWS_AS(j.erase(j.begin(), j.end()), std::domain_error); + CHECK_THROWS_WITH(j.erase(j.begin(), j.end()), "cannot use erase() with null"); + } + { + json j; + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cend()), std::domain_error); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cend()), "cannot use erase() with null"); + } + } + + SECTION("string") + { + { + json j = "foo"; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = "bar"; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = true; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 17; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 17u; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 23.42; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + } + + SECTION("erase with two invalid iterators") + { + SECTION("string") + { + { + json j = "foo"; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = "bar"; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = true; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 17; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 23.42; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + } + } +} diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp new file mode 100644 index 00000000..25fcb535 --- /dev/null +++ b/test/src/unit-inspection.cpp @@ -0,0 +1,373 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("object inspection") +{ + SECTION("convenience type checker") + { + SECTION("object") + { + json j {{"foo", 1}, {"bar", false}}; + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(not j.is_primitive()); + CHECK(j.is_structured()); + } + + SECTION("array") + { + json j {"foo", 1, 1u, 42.23, false}; + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(not j.is_primitive()); + CHECK(j.is_structured()); + } + + SECTION("null") + { + json j(nullptr); + CHECK(j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(j.is_primitive()); + CHECK(not j.is_structured()); + } + + SECTION("boolean") + { + json j(true); + CHECK(not j.is_null()); + CHECK(j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(j.is_primitive()); + CHECK(not j.is_structured()); + } + + SECTION("string") + { + json j("Hello world"); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(j.is_string()); + CHECK(not j.is_discarded()); + CHECK(j.is_primitive()); + CHECK(not j.is_structured()); + } + + SECTION("number (integer)") + { + json j(42); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(j.is_number()); + CHECK(j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(j.is_primitive()); + CHECK(not j.is_structured()); + } + + SECTION("number (unsigned)") + { + json j(42u); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(j.is_number()); + CHECK(j.is_number_integer()); + CHECK(j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(j.is_primitive()); + CHECK(not j.is_structured()); + } + + SECTION("number (floating-point)") + { + json j(42.23); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(j.is_primitive()); + CHECK(not j.is_structured()); + } + + SECTION("discarded") + { + json j(json::value_t::discarded); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(j.is_discarded()); + CHECK(not j.is_primitive()); + CHECK(not j.is_structured()); + } + } + + SECTION("serialization") + { + json j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; + + SECTION("no indent / indent=-1") + { + CHECK(j.dump() == + "{\"array\":[1,2,3,4],\"boolean\":false,\"null\":null,\"number\":42,\"object\":{},\"string\":\"Hello world\"}"); + + CHECK(j.dump() == j.dump(-1)); + } + + SECTION("indent=0") + { + CHECK(j.dump(0) == + "{\n\"array\": [\n1,\n2,\n3,\n4\n],\n\"boolean\": false,\n\"null\": null,\n\"number\": 42,\n\"object\": {},\n\"string\": \"Hello world\"\n}"); + } + + SECTION("indent=4") + { + CHECK(j.dump(4) == + "{\n \"array\": [\n 1,\n 2,\n 3,\n 4\n ],\n \"boolean\": false,\n \"null\": null,\n \"number\": 42,\n \"object\": {},\n \"string\": \"Hello world\"\n}"); + } + + SECTION("dump and floating-point numbers") + { + auto s = json(42.23).dump(); + CHECK(s.find("42.23") != std::string::npos); + } + + SECTION("dump and small floating-point numbers") + { + auto s = json(1.23456e-78).dump(); + CHECK(s.find("1.23456e-78") != std::string::npos); + } + + SECTION("dump and non-ASCII characters") + { + CHECK(json("ä").dump() == "\"ä\""); + CHECK(json("Ö").dump() == "\"Ö\""); + CHECK(json("❤️").dump() == "\"❤️\""); + } + + SECTION("serialization of discarded element") + { + json j_discarded(json::value_t::discarded); + CHECK(j_discarded.dump() == ""); + } + + SECTION("check that precision is reset after serialization") + { + // create stringstream and set precision + std::stringstream ss; + ss.precision(3); + ss << 3.141592653589793 << std::fixed; + CHECK(ss.str() == "3.14"); + + // reset stringstream + ss.str(std::string()); + + // use stringstream for JSON serialization + json j_number = 3.141592653589793; + ss << j_number; + + // check that precision has been overridden during serialization + CHECK(ss.str() == "3.141592653589793"); + + // check that precision has been restored + CHECK(ss.precision() == 3); + } + } + + SECTION("return the type of the object (explicit)") + { + SECTION("null") + { + json j = nullptr; + CHECK(j.type() == json::value_t::null); + } + + SECTION("object") + { + json j = {{"foo", "bar"}}; + CHECK(j.type() == json::value_t::object); + } + + SECTION("array") + { + json j = {1, 2, 3, 4}; + CHECK(j.type() == json::value_t::array); + } + + SECTION("boolean") + { + json j = true; + CHECK(j.type() == json::value_t::boolean); + } + + SECTION("string") + { + json j = "Hello world"; + CHECK(j.type() == json::value_t::string); + } + + SECTION("number (integer)") + { + json j = 23; + CHECK(j.type() == json::value_t::number_integer); + } + + SECTION("number (unsigned)") + { + json j = 23u; + CHECK(j.type() == json::value_t::number_unsigned); + } + + SECTION("number (floating-point)") + { + json j = 42.23; + CHECK(j.type() == json::value_t::number_float); + } + } + + SECTION("return the type of the object (implicit)") + { + SECTION("null") + { + json j = nullptr; + json::value_t t = j; + CHECK(t == j.type()); + } + + SECTION("object") + { + json j = {{"foo", "bar"}}; + json::value_t t = j; + CHECK(t == j.type()); + } + + SECTION("array") + { + json j = {1, 2, 3, 4}; + json::value_t t = j; + CHECK(t == j.type()); + } + + SECTION("boolean") + { + json j = true; + json::value_t t = j; + CHECK(t == j.type()); + } + + SECTION("string") + { + json j = "Hello world"; + json::value_t t = j; + CHECK(t == j.type()); + } + + SECTION("number (integer)") + { + json j = 23; + json::value_t t = j; + CHECK(t == j.type()); + } + + SECTION("number (unsigned)") + { + json j = 23u; + json::value_t t = j; + CHECK(t == j.type()); + } + + SECTION("number (floating-point)") + { + json j = 42.23; + json::value_t t = j; + CHECK(t == j.type()); + } + } +} diff --git a/test/src/unit-iterator_wrapper.cpp b/test/src/unit-iterator_wrapper.cpp new file mode 100644 index 00000000..5d676414 --- /dev/null +++ b/test/src/unit-iterator_wrapper.cpp @@ -0,0 +1,729 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("iterator_wrapper") +{ + SECTION("object") + { + SECTION("value") + { + json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("reference") + { + json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + + // change the value + i.value() = json(11); + CHECK(i.value() == json(11)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + + // change the value + i.value() = json(22); + CHECK(i.value() == json(22)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + + // check if values where changed + CHECK(j == json({{"A", 11}, {"B", 22}})); + } + + SECTION("const value") + { + json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const reference") + { + json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + } + + SECTION("const object") + { + SECTION("value") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("reference") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const value") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const reference") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + } + + SECTION("array") + { + SECTION("value") + { + json j = {"A", "B"}; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("reference") + { + json j = {"A", "B"}; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + + // change the value + i.value() = "AA"; + CHECK(i.value() == "AA"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + + // change the value + i.value() = "BB"; + CHECK(i.value() == "BB"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + + // check if values where changed + CHECK(j == json({"AA", "BB"})); + } + + SECTION("const value") + { + json j = {"A", "B"}; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const reference") + { + json j = {"A", "B"}; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + } + + SECTION("const array") + { + SECTION("value") + { + const json j = {"A", "B"}; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("reference") + { + const json j = {"A", "B"}; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const value") + { + const json j = {"A", "B"}; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const reference") + { + const json j = {"A", "B"}; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + } + + SECTION("primitive") + { + SECTION("value") + { + json j = 1; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("reference") + { + json j = 1; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + + // change value + i.value() = json(2); + } + + CHECK(counter == 2); + + // check if value has changed + CHECK(j == json(2)); + } + + SECTION("const value") + { + json j = 1; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("const reference") + { + json j = 1; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + } + + SECTION("const primitive") + { + SECTION("value") + { + const json j = 1; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("reference") + { + const json j = 1; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("const value") + { + const json j = 1; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("const reference") + { + const json j = 1; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + } +} diff --git a/test/src/unit-iterators.cpp b/test/src/unit-iterators.cpp new file mode 100644 index 00000000..06dcb6aa --- /dev/null +++ b/test/src/unit-iterators.cpp @@ -0,0 +1,2352 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("iterators") +{ + SECTION("basic behavior") + { + SECTION("uninitialized") + { + json::iterator it; + CHECK(it.m_object == nullptr); + + json::const_iterator cit; + CHECK(cit.m_object == nullptr); + } + + SECTION("boolean") + { + json j = true; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); + + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK(it.value() == json(true)); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK(cit.value() == json(true)); + + auto rit = j.rend(); + auto crit = j.crend(); + CHECK_THROWS_AS(rit.key(), std::domain_error); + CHECK_THROWS_AS(rit.value(), std::out_of_range); + CHECK_THROWS_AS(crit.key(), std::domain_error); + CHECK_THROWS_AS(crit.value(), std::out_of_range); + CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "cannot get value"); + CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "cannot get value"); + } + } + + SECTION("string") + { + json j = "hello world"; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); + + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK(it.value() == json("hello world")); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK(cit.value() == json("hello world")); + + auto rit = j.rend(); + auto crit = j.crend(); + CHECK_THROWS_AS(rit.key(), std::domain_error); + CHECK_THROWS_AS(rit.value(), std::out_of_range); + CHECK_THROWS_AS(crit.key(), std::domain_error); + CHECK_THROWS_AS(crit.value(), std::out_of_range); + CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "cannot get value"); + CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "cannot get value"); + } + } + + SECTION("array") + { + json j = {1, 2, 3}; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it_begin = j.begin(); + json::iterator it_end = j.end(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[0]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[2]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + begin/end") + { + json::const_iterator it_begin = j_const.begin(); + json::const_iterator it_end = j_const.end(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j_const[0]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const[2]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it_begin = j.cbegin(); + json::const_iterator it_end = j.cend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[0]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[2]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it_begin = j_const.cbegin(); + json::const_iterator it_end = j_const.cend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[0]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[2]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it_begin = j.rbegin(); + json::reverse_iterator it_end = j.rend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[2]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[0]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it_begin = j.crbegin(); + json::const_reverse_iterator it_end = j.crend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[2]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[0]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it_begin = j_const.crbegin(); + json::const_reverse_iterator it_end = j_const.crend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[2]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[0]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK(it.value() == json(1)); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK(cit.value() == json(1)); + } + } + + SECTION("object") + { + json j = {{"A", 1}, {"B", 2}, {"C", 3}}; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it_begin = j.begin(); + json::iterator it_end = j.end(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["A"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["C"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + begin/end") + { + json::const_iterator it_begin = j_const.begin(); + json::const_iterator it_end = j_const.end(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j_const["A"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const["C"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it_begin = j.cbegin(); + json::const_iterator it_end = j.cend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["A"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["C"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it_begin = j_const.cbegin(); + json::const_iterator it_end = j_const.cend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j_const["A"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const["C"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it_begin = j.rbegin(); + json::reverse_iterator it_end = j.rend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["C"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["A"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it_begin = j.crbegin(); + json::const_reverse_iterator it_end = j.crend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["C"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["A"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it_begin = j_const.crbegin(); + json::const_reverse_iterator it_end = j_const.crend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["C"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["A"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK(it.key() == "A"); + CHECK(it.value() == json(1)); + CHECK(cit.key() == "A"); + CHECK(cit.value() == json(1)); + } + } + + SECTION("number (integer)") + { + json j = 23; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); + + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK(it.value() == json(23)); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK(cit.value() == json(23)); + + auto rit = j.rend(); + auto crit = j.crend(); + CHECK_THROWS_AS(rit.key(), std::domain_error); + CHECK_THROWS_AS(rit.value(), std::out_of_range); + CHECK_THROWS_AS(crit.key(), std::domain_error); + CHECK_THROWS_AS(crit.value(), std::out_of_range); + CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "cannot get value"); + CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "cannot get value"); + } + } + + SECTION("number (unsigned)") + { + json j = 23u; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); + + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK(it.value() == json(23)); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK(cit.value() == json(23)); + + auto rit = j.rend(); + auto crit = j.crend(); + CHECK_THROWS_AS(rit.key(), std::domain_error); + CHECK_THROWS_AS(rit.value(), std::out_of_range); + CHECK_THROWS_AS(crit.key(), std::domain_error); + CHECK_THROWS_AS(crit.value(), std::out_of_range); + CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "cannot get value"); + CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "cannot get value"); + } + } + + SECTION("number (float)") + { + json j = 23.42; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); + + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK(it.value() == json(23.42)); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK(cit.value() == json(23.42)); + + auto rit = j.rend(); + auto crit = j.crend(); + CHECK_THROWS_AS(rit.key(), std::domain_error); + CHECK_THROWS_AS(rit.value(), std::out_of_range); + CHECK_THROWS_AS(crit.key(), std::domain_error); + CHECK_THROWS_AS(crit.value(), std::out_of_range); + CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "cannot get value"); + CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "cannot get value"); + } + } + + SECTION("null") + { + json j = nullptr; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it == j.end()); + } + + SECTION("const json + begin/end") + { + json::const_iterator it_begin = j_const.begin(); + json::const_iterator it_end = j_const.end(); + CHECK(it_begin == it_end); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it_begin = j.cbegin(); + json::const_iterator it_end = j.cend(); + CHECK(it_begin == it_end); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it_begin = j_const.cbegin(); + json::const_iterator it_end = j_const.cend(); + CHECK(it_begin == it_end); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it == j.rend()); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it == j.crend()); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it == j_const.crend()); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_AS(it.value(), std::out_of_range); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_AS(cit.value(), std::out_of_range); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(it.value(), "cannot get value"); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(cit.value(), "cannot get value"); + + auto rit = j.rend(); + auto crit = j.crend(); + CHECK_THROWS_AS(rit.key(), std::domain_error); + CHECK_THROWS_AS(rit.value(), std::out_of_range); + CHECK_THROWS_AS(crit.key(), std::domain_error); + CHECK_THROWS_AS(crit.value(), std::out_of_range); + CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "cannot get value"); + CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "cannot get value"); + } + } + } + + SECTION("iterator comparisons") + { + json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; + + for (json& j : j_values) + { + auto it1 = j.begin(); + auto it2 = j.begin(); + auto it3 = j.begin(); + ++it2; + ++it3; + ++it3; + auto it1_c = j.cbegin(); + auto it2_c = j.cbegin(); + auto it3_c = j.cbegin(); + ++it2_c; + ++it3_c; + ++it3_c; + + // comparison: equal + { + CHECK(it1 == it1); + CHECK(not (it1 == it2)); + CHECK(not (it1 == it3)); + CHECK(not (it2 == it3)); + CHECK(it1_c == it1_c); + CHECK(not (it1_c == it2_c)); + CHECK(not (it1_c == it3_c)); + CHECK(not (it2_c == it3_c)); + } + + // comparison: not equal + { + // check definition + CHECK( (it1 != it1) == not(it1 == it1) ); + CHECK( (it1 != it2) == not(it1 == it2) ); + CHECK( (it1 != it3) == not(it1 == it3) ); + CHECK( (it2 != it3) == not(it2 == it3) ); + CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); + CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); + CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); + CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); + } + + // comparison: smaller + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 < it1, std::domain_error); + CHECK_THROWS_AS(it1 < it2, std::domain_error); + CHECK_THROWS_AS(it2 < it3, std::domain_error); + CHECK_THROWS_AS(it1 < it3, std::domain_error); + CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); + } + else + { + CHECK(not (it1 < it1)); + CHECK(it1 < it2); + CHECK(it1 < it3); + CHECK(it2 < it3); + CHECK(not (it1_c < it1_c)); + CHECK(it1_c < it2_c); + CHECK(it1_c < it3_c); + CHECK(it2_c < it3_c); + } + } + + // comparison: less than or equal + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 <= it1, std::domain_error); + CHECK_THROWS_AS(it1 <= it2, std::domain_error); + CHECK_THROWS_AS(it2 <= it3, std::domain_error); + CHECK_THROWS_AS(it1 <= it3, std::domain_error); + CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 <= it1) == not(it1 < it1) ); + CHECK( (it1 <= it2) == not(it2 < it1) ); + CHECK( (it1 <= it3) == not(it3 < it1) ); + CHECK( (it2 <= it3) == not(it3 < it2) ); + CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); + CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); + CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); + CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); + } + } + + // comparison: greater than + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 > it1, std::domain_error); + CHECK_THROWS_AS(it1 > it2, std::domain_error); + CHECK_THROWS_AS(it2 > it3, std::domain_error); + CHECK_THROWS_AS(it1 > it3, std::domain_error); + CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 > it1) == (it1 < it1) ); + CHECK( (it1 > it2) == (it2 < it1) ); + CHECK( (it1 > it3) == (it3 < it1) ); + CHECK( (it2 > it3) == (it3 < it2) ); + CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); + CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); + CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); + CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); + } + } + + // comparison: greater than or equal + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 >= it1, std::domain_error); + CHECK_THROWS_AS(it1 >= it2, std::domain_error); + CHECK_THROWS_AS(it2 >= it3, std::domain_error); + CHECK_THROWS_AS(it1 >= it3, std::domain_error); + CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 >= it1) == not(it1 < it1) ); + CHECK( (it1 >= it2) == not(it1 < it2) ); + CHECK( (it1 >= it3) == not(it1 < it3) ); + CHECK( (it2 >= it3) == not(it2 < it3) ); + CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); + CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); + CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); + CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); + } + } + } + + // check exceptions if different objects are compared + for (auto j : j_values) + { + for (auto k : j_values) + { + if (j != k) + { + CHECK_THROWS_AS(j.begin() == k.begin(), std::domain_error); + CHECK_THROWS_AS(j.cbegin() == k.cbegin(), std::domain_error); + CHECK_THROWS_WITH(j.begin() == k.begin(), "cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "cannot compare iterators of different containers"); + + CHECK_THROWS_AS(j.begin() < k.begin(), std::domain_error); + CHECK_THROWS_AS(j.cbegin() < k.cbegin(), std::domain_error); + CHECK_THROWS_WITH(j.begin() < k.begin(), "cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "cannot compare iterators of different containers"); + } + } + } + } + + SECTION("iterator arithmetic") + { + json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j_array = {1, 2, 3, 4, 5, 6}; + json j_null = nullptr; + json j_value = 42; + + SECTION("addition and subtraction") + { + SECTION("object") + { + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it += 1, std::domain_error); + CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it += 1, std::domain_error); + CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it + 1, std::domain_error); + CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it + 1, std::domain_error); + CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it -= 1, std::domain_error); + CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it -= 1, std::domain_error); + CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it - 1, std::domain_error); + CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it - 1, std::domain_error); + CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it - it, std::domain_error); + CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it - it, std::domain_error); + CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + } + } + + SECTION("array") + { + { + auto it = j_array.begin(); + it += 3; + CHECK((j_array.begin() + 3) == it); + CHECK((it - 3) == j_array.begin()); + CHECK((it - j_array.begin()) == 3); + CHECK(*it == json(4)); + it -= 2; + CHECK(*it == json(2)); + } + { + auto it = j_array.cbegin(); + it += 3; + CHECK((j_array.cbegin() + 3) == it); + CHECK((it - 3) == j_array.cbegin()); + CHECK((it - j_array.cbegin()) == 3); + CHECK(*it == json(4)); + it -= 2; + CHECK(*it == json(2)); + } + } + + SECTION("null") + { + { + auto it = j_null.begin(); + it += 3; + CHECK((j_null.begin() + 3) == it); + CHECK((it - 3) == j_null.begin()); + CHECK((it - j_null.begin()) == 3); + CHECK(it != j_null.end()); + it -= 3; + CHECK(it == j_null.end()); + } + { + auto it = j_null.cbegin(); + it += 3; + CHECK((j_null.cbegin() + 3) == it); + CHECK((it - 3) == j_null.cbegin()); + CHECK((it - j_null.cbegin()) == 3); + CHECK(it != j_null.cend()); + it -= 3; + CHECK(it == j_null.cend()); + } + } + + SECTION("value") + { + { + auto it = j_value.begin(); + it += 3; + CHECK((j_value.begin() + 3) == it); + CHECK((it - 3) == j_value.begin()); + CHECK((it - j_value.begin()) == 3); + CHECK(it != j_value.end()); + it -= 3; + CHECK(*it == json(42)); + } + { + auto it = j_value.cbegin(); + it += 3; + CHECK((j_value.cbegin() + 3) == it); + CHECK((it - 3) == j_value.cbegin()); + CHECK((it - j_value.cbegin()) == 3); + CHECK(it != j_value.cend()); + it -= 3; + CHECK(*it == json(42)); + } + } + } + + SECTION("subscript operator") + { + SECTION("object") + { + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it[0], std::domain_error); + CHECK_THROWS_AS(it[1], std::domain_error); + CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); + CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it[0], std::domain_error); + CHECK_THROWS_AS(it[1], std::domain_error); + CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); + CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); + } + } + + SECTION("array") + { + { + auto it = j_array.begin(); + CHECK(it[0] == json(1)); + CHECK(it[1] == json(2)); + CHECK(it[2] == json(3)); + CHECK(it[3] == json(4)); + CHECK(it[4] == json(5)); + CHECK(it[5] == json(6)); + } + { + auto it = j_array.cbegin(); + CHECK(it[0] == json(1)); + CHECK(it[1] == json(2)); + CHECK(it[2] == json(3)); + CHECK(it[3] == json(4)); + CHECK(it[4] == json(5)); + CHECK(it[5] == json(6)); + } + } + + SECTION("null") + { + { + auto it = j_null.begin(); + CHECK_THROWS_AS(it[0], std::out_of_range); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[0], "cannot get value"); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + { + auto it = j_null.cbegin(); + CHECK_THROWS_AS(it[0], std::out_of_range); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[0], "cannot get value"); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + } + + SECTION("value") + { + { + auto it = j_value.begin(); + CHECK(it[0] == json(42)); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + { + auto it = j_value.cbegin(); + CHECK(it[0] == json(42)); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + } + } + } + + SECTION("reverse iterator comparisons") + { + json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; + + for (json& j : j_values) + { + auto it1 = j.rbegin(); + auto it2 = j.rbegin(); + auto it3 = j.rbegin(); + ++it2; + ++it3; + ++it3; + auto it1_c = j.crbegin(); + auto it2_c = j.crbegin(); + auto it3_c = j.crbegin(); + ++it2_c; + ++it3_c; + ++it3_c; + + // comparison: equal + { + CHECK(it1 == it1); + CHECK(not (it1 == it2)); + CHECK(not (it1 == it3)); + CHECK(not (it2 == it3)); + CHECK(it1_c == it1_c); + CHECK(not (it1_c == it2_c)); + CHECK(not (it1_c == it3_c)); + CHECK(not (it2_c == it3_c)); + } + + // comparison: not equal + { + // check definition + CHECK( (it1 != it1) == not(it1 == it1) ); + CHECK( (it1 != it2) == not(it1 == it2) ); + CHECK( (it1 != it3) == not(it1 == it3) ); + CHECK( (it2 != it3) == not(it2 == it3) ); + CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); + CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); + CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); + CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); + } + + // comparison: smaller + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 < it1, std::domain_error); + CHECK_THROWS_AS(it1 < it2, std::domain_error); + CHECK_THROWS_AS(it2 < it3, std::domain_error); + CHECK_THROWS_AS(it1 < it3, std::domain_error); + CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); + } + else + { + CHECK(not (it1 < it1)); + CHECK(it1 < it2); + CHECK(it1 < it3); + CHECK(it2 < it3); + CHECK(not (it1_c < it1_c)); + CHECK(it1_c < it2_c); + CHECK(it1_c < it3_c); + CHECK(it2_c < it3_c); + } + } + + // comparison: less than or equal + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 <= it1, std::domain_error); + CHECK_THROWS_AS(it1 <= it2, std::domain_error); + CHECK_THROWS_AS(it2 <= it3, std::domain_error); + CHECK_THROWS_AS(it1 <= it3, std::domain_error); + CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 <= it1) == not(it1 < it1) ); + CHECK( (it1 <= it2) == not(it2 < it1) ); + CHECK( (it1 <= it3) == not(it3 < it1) ); + CHECK( (it2 <= it3) == not(it3 < it2) ); + CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); + CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); + CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); + CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); + } + } + + // comparison: greater than + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 > it1, std::domain_error); + CHECK_THROWS_AS(it1 > it2, std::domain_error); + CHECK_THROWS_AS(it2 > it3, std::domain_error); + CHECK_THROWS_AS(it1 > it3, std::domain_error); + CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 > it1) == (it1 < it1) ); + CHECK( (it1 > it2) == (it2 < it1) ); + CHECK( (it1 > it3) == (it3 < it1) ); + CHECK( (it2 > it3) == (it3 < it2) ); + CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); + CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); + CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); + CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); + } + } + + // comparison: greater than or equal + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 >= it1, std::domain_error); + CHECK_THROWS_AS(it1 >= it2, std::domain_error); + CHECK_THROWS_AS(it2 >= it3, std::domain_error); + CHECK_THROWS_AS(it1 >= it3, std::domain_error); + CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 >= it1) == not(it1 < it1) ); + CHECK( (it1 >= it2) == not(it1 < it2) ); + CHECK( (it1 >= it3) == not(it1 < it3) ); + CHECK( (it2 >= it3) == not(it2 < it3) ); + CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); + CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); + CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); + CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); + } + } + } + + // check exceptions if different objects are compared + for (auto j : j_values) + { + for (auto k : j_values) + { + if (j != k) + { + CHECK_THROWS_AS(j.rbegin() == k.rbegin(), std::domain_error); + CHECK_THROWS_AS(j.crbegin() == k.crbegin(), std::domain_error); + CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "cannot compare iterators of different containers"); + + CHECK_THROWS_AS(j.rbegin() < k.rbegin(), std::domain_error); + CHECK_THROWS_AS(j.crbegin() < k.crbegin(), std::domain_error); + CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "cannot compare iterators of different containers"); + } + } + } + } + + SECTION("reverse iterator arithmetic") + { + json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j_array = {1, 2, 3, 4, 5, 6}; + json j_null = nullptr; + json j_value = 42; + + SECTION("addition and subtraction") + { + SECTION("object") + { + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it += 1, std::domain_error); + CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it += 1, std::domain_error); + CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it + 1, std::domain_error); + CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it + 1, std::domain_error); + CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it -= 1, std::domain_error); + CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it -= 1, std::domain_error); + CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it - 1, std::domain_error); + CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it - 1, std::domain_error); + CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it - it, std::domain_error); + CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it - it, std::domain_error); + CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + } + } + + SECTION("array") + { + { + auto it = j_array.rbegin(); + it += 3; + CHECK((j_array.rbegin() + 3) == it); + CHECK((it - 3) == j_array.rbegin()); + CHECK((j_array.rbegin() - it) == 3); + CHECK(*it == json(3)); + it -= 2; + CHECK(*it == json(5)); + } + { + auto it = j_array.crbegin(); + it += 3; + CHECK((j_array.crbegin() + 3) == it); + CHECK((it - 3) == j_array.crbegin()); + CHECK((j_array.crbegin() - it) == 3); + CHECK(*it == json(3)); + it -= 2; + CHECK(*it == json(5)); + } + } + + SECTION("null") + { + { + auto it = j_null.rbegin(); + it += 3; + CHECK((j_null.rbegin() + 3) == it); + CHECK((it - 3) == j_null.rbegin()); + CHECK((j_null.rbegin() - it) == 3); + CHECK(it != j_null.rend()); + it -= 3; + CHECK(it == j_null.rend()); + } + { + auto it = j_null.crbegin(); + it += 3; + CHECK((j_null.crbegin() + 3) == it); + CHECK((it - 3) == j_null.crbegin()); + CHECK((j_null.crbegin() - it) == 3); + CHECK(it != j_null.crend()); + it -= 3; + CHECK(it == j_null.crend()); + } + } + + SECTION("value") + { + { + auto it = j_value.rbegin(); + it += 3; + CHECK((j_value.rbegin() + 3) == it); + CHECK((it - 3) == j_value.rbegin()); + CHECK((j_value.rbegin() - it) == 3); + CHECK(it != j_value.rend()); + it -= 3; + CHECK(*it == json(42)); + } + { + auto it = j_value.crbegin(); + it += 3; + CHECK((j_value.crbegin() + 3) == it); + CHECK((it - 3) == j_value.crbegin()); + CHECK((j_value.crbegin() - it) == 3); + CHECK(it != j_value.crend()); + it -= 3; + CHECK(*it == json(42)); + } + } + } + + SECTION("subscript operator") + { + SECTION("object") + { + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it[0], std::domain_error); + CHECK_THROWS_AS(it[1], std::domain_error); + CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); + CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it[0], std::domain_error); + CHECK_THROWS_AS(it[1], std::domain_error); + CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); + CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); + } + } + + SECTION("array") + { + { + auto it = j_array.rbegin(); + CHECK(it[0] == json(6)); + CHECK(it[1] == json(5)); + CHECK(it[2] == json(4)); + CHECK(it[3] == json(3)); + CHECK(it[4] == json(2)); + CHECK(it[5] == json(1)); + } + { + auto it = j_array.crbegin(); + CHECK(it[0] == json(6)); + CHECK(it[1] == json(5)); + CHECK(it[2] == json(4)); + CHECK(it[3] == json(3)); + CHECK(it[4] == json(2)); + CHECK(it[5] == json(1)); + } + } + + SECTION("null") + { + { + auto it = j_null.rbegin(); + CHECK_THROWS_AS(it[0], std::out_of_range); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[0], "cannot get value"); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + { + auto it = j_null.crbegin(); + CHECK_THROWS_AS(it[0], std::out_of_range); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[0], "cannot get value"); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + } + + SECTION("value") + { + { + auto it = j_value.rbegin(); + CHECK(it[0] == json(42)); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + { + auto it = j_value.crbegin(); + CHECK(it[0] == json(42)); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + } + } + } +} diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp new file mode 100644 index 00000000..af4d4547 --- /dev/null +++ b/test/src/unit-json_patch.cpp @@ -0,0 +1,1217 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("JSON patch") +{ + SECTION("examples from RFC 6902") + { + SECTION("4. Operations") + { + // the ordering of members in JSON objects is not significant: + json op1 = R"({ "op": "add", "path": "/a/b/c", "value": "foo" })"_json; + json op2 = R"({ "path": "/a/b/c", "op": "add", "value": "foo" })"_json; + json op3 = R"({ "value": "foo", "path": "/a/b/c", "op": "add" })"_json; + + // check if the operation objects are equivalent + CHECK(op1 == op2); + CHECK(op1 == op3); + } + + SECTION("4.1 add") + { + json patch = R"([{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }])"_json; + + // However, the object itself or an array containing it does need + // to exist, and it remains an error for that not to be the case. + // For example, an "add" with a target location of "/a/b" starting + // with this document + json doc1 = R"({ "a": { "foo": 1 } })"_json; + + // is not an error, because "a" exists, and "b" will be added to + // its value. + CHECK_NOTHROW(doc1.patch(patch)); + CHECK(doc1.patch(patch) == R"( + { + "a": { + "foo": 1, + "b": { + "c": [ "foo", "bar" ] + } + } + } + )"_json); + + // It is an error in this document: + json doc2 = R"({ "q": { "bar": 2 } })"_json; + + // because "a" does not exist. + CHECK_THROWS_AS(doc2.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(doc2.patch(patch), "key 'a' not found"); + } + + SECTION("4.2 remove") + { + // If removing an element from an array, any elements above the + // specified index are shifted one position to the left. + json doc = {1, 2, 3, 4}; + json patch = {{{"op", "remove"}, {"path", "/1"}}}; + CHECK(doc.patch(patch) == json({1, 3, 4})); + } + + SECTION("A.1. Adding an Object Member") + { + // An example target JSON document: + json doc = R"( + { "foo": "bar"} + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "/baz", "value": "qux" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { + "baz": "qux", + "foo": "bar" + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.2. Adding an Array Element") + { + // An example target JSON document: + json doc = R"( + { "foo": [ "bar", "baz" ] } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "/foo/1", "value": "qux" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { "foo": [ "bar", "qux", "baz" ] } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.3. Removing an Object Member") + { + // An example target JSON document: + json doc = R"( + { + "baz": "qux", + "foo": "bar" + } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "remove", "path": "/baz" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { "foo": "bar" } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.4. Removing an Array Element") + { + // An example target JSON document: + json doc = R"( + { "foo": [ "bar", "qux", "baz" ] } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "remove", "path": "/foo/1" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { "foo": [ "bar", "baz" ] } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.5. Replacing a Value") + { + // An example target JSON document: + json doc = R"( + { + "baz": "qux", + "foo": "bar" + } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "replace", "path": "/baz", "value": "boo" } + ] + )"_json; + + json expected = R"( + { + "baz": "boo", + "foo": "bar" + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.6. Moving a Value") + { + // An example target JSON document: + json doc = R"( + { + "foo": { + "bar": "baz", + "waldo": "fred" + }, + "qux": { + "corge": "grault" + } + } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "move", "from": "/foo/waldo", "path": "/qux/thud" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { + "foo": { + "bar": "baz" + }, + "qux": { + "corge": "grault", + "thud": "fred" + } + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.7. Moving a Value") + { + // An example target JSON document: + json doc = R"( + { "foo": [ "all", "grass", "cows", "eat" ] } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "move", "from": "/foo/1", "path": "/foo/3" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { "foo": [ "all", "cows", "eat", "grass" ] } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.8. Testing a Value: Success") + { + // An example target JSON document: + json doc = R"( + { + "baz": "qux", + "foo": [ "a", 2, "c" ] + } + )"_json; + + // A JSON Patch document that will result in successful evaluation: + json patch = R"( + [ + { "op": "test", "path": "/baz", "value": "qux" }, + { "op": "test", "path": "/foo/1", "value": 2 } + ] + )"_json; + + // check if evaluation does not throw + CHECK_NOTHROW(doc.patch(patch)); + // check if patched document is unchanged + CHECK(doc.patch(patch) == doc); + } + + SECTION("A.9. Testing a Value: Error") + { + // An example target JSON document: + json doc = R"( + { "baz": "qux" } + )"_json; + + // A JSON Patch document that will result in an error condition: + json patch = R"( + [ + { "op": "test", "path": "/baz", "value": "bar" } + ] + )"_json; + + // check that evaluation throws + CHECK_THROWS_AS(doc.patch(patch), std::domain_error); + CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); + } + + SECTION("A.10. Adding a Nested Member Object") + { + // An example target JSON document: + json doc = R"( + { "foo": "bar" } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "/child", "value": { "grandchild": { } } } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { + "foo": "bar", + "child": { + "grandchild": { + } + } + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.11. Ignoring Unrecognized Elements") + { + // An example target JSON document: + json doc = R"( + { "foo": "bar" } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "/baz", "value": "qux", "xyz": 123 } + ] + )"_json; + + json expected = R"( + { + "foo": "bar", + "baz": "qux" + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.12. Adding to a Nonexistent Target") + { + // An example target JSON document: + json doc = R"( + { "foo": "bar" } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "/baz/bat", "value": "qux" } + ] + )"_json; + + // This JSON Patch document, applied to the target JSON document + // above, would result in an error (therefore, it would not be + // applied), because the "add" operation's target location that + // references neither the root of the document, nor a member of + // an existing object, nor a member of an existing array. + + CHECK_THROWS_AS(doc.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(doc.patch(patch), "key 'baz' not found"); + } + + // A.13. Invalid JSON Patch Document + // not applicable + + SECTION("A.14. Escape Ordering") + { + // An example target JSON document: + json doc = R"( + { + "/": 9, + "~1": 10 + } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + {"op": "test", "path": "/~01", "value": 10} + ] + )"_json; + + json expected = R"( + { + "/": 9, + "~1": 10 + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.15. Comparing Strings and Numbers") + { + // An example target JSON document: + json doc = R"( + { + "/": 9, + "~1": 10 + } + )"_json; + + // A JSON Patch document that will result in an error condition: + json patch = R"( + [ + {"op": "test", "path": "/~01", "value": "10"} + ] + )"_json; + + // check that evaluation throws + CHECK_THROWS_AS(doc.patch(patch), std::domain_error); + CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); + } + + SECTION("A.16. Adding an Array Value") + { + // An example target JSON document: + json doc = R"( + { "foo": ["bar"] } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "/foo/-", "value": ["abc", "def"] } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { "foo": ["bar", ["abc", "def"]] } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + } + + SECTION("own examples") + { + SECTION("add") + { + SECTION("add to the root element") + { + // If the path is the root of the target document - the + // specified value becomes the entire content of the target + // document. + + // An example target JSON document: + json doc = 17; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "", "value": [1,2,3] } + ] + )"_json; + + // The resulting JSON document: + json expected = {1, 2, 3}; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("add to end of the array") + { + // The specified index MUST NOT be greater than the number of + // elements in the array. The example below uses and index of + // exactly the number of elements in the array which is legal. + + // An example target JSON document: + json doc = {0, 1, 2}; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "/3", "value": 3 } + ] + )"_json; + + // The resulting JSON document: + json expected = {0, 1, 2, 3}; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + } + + SECTION("copy") + { + // An example target JSON document: + json doc = R"( + { + "foo": { + "bar": "baz", + "waldo": "fred" + }, + "qux": { + "corge": "grault" + } + } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "copy", "from": "/foo/waldo", "path": "/qux/thud" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { + "foo": { + "bar": "baz", + "waldo": "fred" + }, + "qux": { + "corge": "grault", + "thud": "fred" + } + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("replace") + { + json j = "string"; + json patch = {{{"op", "replace"}, {"path", ""}, {"value", 1}}}; + CHECK(j.patch(patch) == json(1)); + } + + SECTION("documentation GIF") + { + { + // a JSON patch + json p1 = R"( + [{"op": "add", "path": "/GB", "value": "London"}] + )"_json; + + // a JSON value + json source = R"( + {"D": "Berlin", "F": "Paris"} + )"_json; + + // apply the patch + json target = source.patch(p1); + // target = { "D": "Berlin", "F": "Paris", "GB": "London" } + CHECK(target == R"({ "D": "Berlin", "F": "Paris", "GB": "London" })"_json); + + // create a diff from two JSONs + json p2 = json::diff(target, source); + // p2 = [{"op": "delete", "path": "/GB"}] + CHECK(p2 == R"([{"op":"remove","path":"/GB"}])"_json); + } + { + // a JSON value + json j = {"good", "bad", "ugly"}; + + // a JSON pointer + auto ptr = json::json_pointer("/2"); + + // use to access elements + j[ptr] = {{"it", "cattivo"}}; + CHECK(j == R"(["good","bad",{"it":"cattivo"}])"_json); + + // use user-defined string literal + j["/2/en"_json_pointer] = "ugly"; + CHECK(j == R"(["good","bad",{"en":"ugly","it":"cattivo"}])"_json); + + json flat = j.flatten(); + CHECK(flat == R"({"/0":"good","/1":"bad","/2/en":"ugly","/2/it":"cattivo"})"_json); + } + } + } + + SECTION("errors") + { + SECTION("unknown operation") + { + SECTION("not an array") + { + json j; + json patch = {{"op", "add"}, {"path", ""}, {"value", 1}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects"); + } + + SECTION("not an array of objects") + { + json j; + json patch = {"op", "add", "path", "", "value", 1}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects"); + } + + SECTION("missing 'op'") + { + json j; + json patch = {{{"foo", "bar"}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation must have member 'op'"); + } + + SECTION("non-string 'op'") + { + json j; + json patch = {{{"op", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation must have string member 'op'"); + } + + SECTION("invalid operation") + { + json j; + json patch = {{{"op", "foo"}, {"path", ""}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation value 'foo' is invalid"); + } + } + + SECTION("add") + { + SECTION("missing 'path'") + { + json j; + json patch = {{{"op", "add"}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'path'"); + } + + SECTION("non-string 'path'") + { + json j; + json patch = {{{"op", "add"}, {"path", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have string member 'path'"); + } + + SECTION("missing 'value'") + { + json j; + json patch = {{{"op", "add"}, {"path", ""}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'value'"); + } + + SECTION("invalid array index") + { + json j = {1, 2}; + json patch = {{{"op", "add"}, {"path", "/4"}, {"value", 4}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "array index 4 is out of range"); + } + } + + SECTION("remove") + { + SECTION("missing 'path'") + { + json j; + json patch = {{{"op", "remove"}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have member 'path'"); + } + + SECTION("non-string 'path'") + { + json j; + json patch = {{{"op", "remove"}, {"path", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have string member 'path'"); + } + + SECTION("nonexisting target location (array)") + { + json j = {1, 2, 3}; + json patch = {{{"op", "remove"}, {"path", "/17"}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range"); + } + + SECTION("nonexisting target location (object)") + { + json j = {{"foo", 1}, {"bar", 2}}; + json patch = {{{"op", "remove"}, {"path", "/baz"}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); + } + + SECTION("root element as target location") + { + json j = "string"; + json patch = {{{"op", "remove"}, {"path", ""}}}; + CHECK_THROWS_AS(j.patch(patch), std::domain_error); + CHECK_THROWS_WITH(j.patch(patch), "JSON pointer has no parent"); + } + } + + SECTION("replace") + { + SECTION("missing 'path'") + { + json j; + json patch = {{{"op", "replace"}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'path'"); + } + + SECTION("non-string 'path'") + { + json j; + json patch = {{{"op", "replace"}, {"path", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have string member 'path'"); + } + + SECTION("missing 'value'") + { + json j; + json patch = {{{"op", "replace"}, {"path", ""}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'value'"); + } + + SECTION("nonexisting target location (array)") + { + json j = {1, 2, 3}; + json patch = {{{"op", "replace"}, {"path", "/17"}, {"value", 19}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range"); + } + + SECTION("nonexisting target location (object)") + { + json j = {{"foo", 1}, {"bar", 2}}; + json patch = {{{"op", "replace"}, {"path", "/baz"}, {"value", 3}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); + } + } + + SECTION("move") + { + SECTION("missing 'path'") + { + json j; + json patch = {{{"op", "move"}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'path'"); + } + + SECTION("non-string 'path'") + { + json j; + json patch = {{{"op", "move"}, {"path", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'path'"); + } + + SECTION("missing 'from'") + { + json j; + json patch = {{{"op", "move"}, {"path", ""}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'from'"); + } + + SECTION("non-string 'from'") + { + json j; + json patch = {{{"op", "move"}, {"path", ""}, {"from", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'from'"); + } + + SECTION("nonexisting from location (array)") + { + json j = {1, 2, 3}; + json patch = {{{"op", "move"}, {"path", "/0"}, {"from", "/5"}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range"); + } + + SECTION("nonexisting from location (object)") + { + json j = {{"foo", 1}, {"bar", 2}}; + json patch = {{{"op", "move"}, {"path", "/baz"}, {"from", "/baz"}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); + } + } + + SECTION("copy") + { + SECTION("missing 'path'") + { + json j; + json patch = {{{"op", "copy"}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'path'"); + } + + SECTION("non-string 'path'") + { + json j; + json patch = {{{"op", "copy"}, {"path", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'path'"); + } + + SECTION("missing 'from'") + { + json j; + json patch = {{{"op", "copy"}, {"path", ""}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'from'"); + } + + SECTION("non-string 'from'") + { + json j; + json patch = {{{"op", "copy"}, {"path", ""}, {"from", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'from'"); + } + + SECTION("nonexisting from location (array)") + { + json j = {1, 2, 3}; + json patch = {{{"op", "copy"}, {"path", "/0"}, {"from", "/5"}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range"); + } + + SECTION("nonexisting from location (object)") + { + json j = {{"foo", 1}, {"bar", 2}}; + json patch = {{{"op", "copy"}, {"path", "/fob"}, {"from", "/baz"}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); + } + } + + SECTION("test") + { + SECTION("missing 'path'") + { + json j; + json patch = {{{"op", "test"}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'path'"); + } + + SECTION("non-string 'path'") + { + json j; + json patch = {{{"op", "test"}, {"path", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have string member 'path'"); + } + + SECTION("missing 'value'") + { + json j; + json patch = {{{"op", "test"}, {"path", ""}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'value'"); + } + } + } + + SECTION("Examples from jsonpatch.com") + { + SECTION("Simple Example") + { + // The original document + json doc = R"( + { + "baz": "qux", + "foo": "bar" + } + )"_json; + + // The patch + json patch = R"( + [ + { "op": "replace", "path": "/baz", "value": "boo" }, + { "op": "add", "path": "/hello", "value": ["world"] }, + { "op": "remove", "path": "/foo"} + ] + )"_json; + + // The result + json result = R"( + { + "baz": "boo", + "hello": ["world"] + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == result); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, result)) == result); + } + + SECTION("Operations") + { + // The original document + json doc = R"( + { + "biscuits": [ + {"name":"Digestive"}, + {"name": "Choco Liebniz"} + ] + } + )"_json; + + SECTION("add") + { + // The patch + json patch = R"( + [ + {"op": "add", "path": "/biscuits/1", "value": {"name": "Ginger Nut"}} + ] + )"_json; + + // The result + json result = R"( + { + "biscuits": [ + {"name": "Digestive"}, + {"name": "Ginger Nut"}, + {"name": "Choco Liebniz"} + ] + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == result); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, result)) == result); + } + + SECTION("remove") + { + // The patch + json patch = R"( + [ + {"op": "remove", "path": "/biscuits"} + ] + )"_json; + + // The result + json result = R"( + {} + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == result); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, result)) == result); + } + + SECTION("replace") + { + // The patch + json patch = R"( + [ + {"op": "replace", "path": "/biscuits/0/name", "value": "Chocolate Digestive"} + ] + )"_json; + + // The result + json result = R"( + { + "biscuits": [ + {"name": "Chocolate Digestive"}, + {"name": "Choco Liebniz"} + ] + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == result); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, result)) == result); + } + + SECTION("copy") + { + // The patch + json patch = R"( + [ + {"op": "copy", "from": "/biscuits/0", "path": "/best_biscuit"} + ] + )"_json; + + // The result + json result = R"( + { + "biscuits": [ + {"name": "Digestive"}, + {"name": "Choco Liebniz"} + ], + "best_biscuit": { + "name": "Digestive" + } + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == result); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, result)) == result); + } + + SECTION("move") + { + // The patch + json patch = R"( + [ + {"op": "move", "from": "/biscuits", "path": "/cookies"} + ] + )"_json; + + // The result + json result = R"( + { + "cookies": [ + {"name": "Digestive"}, + {"name": "Choco Liebniz"} + ] + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == result); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, result)) == result); + } + + SECTION("test") + { + // The patch + json patch = R"( + [ + {"op": "test", "path": "/best_biscuit/name", "value": "Choco Liebniz"} + ] + )"_json; + + // the test will fail + CHECK_THROWS_AS(doc.patch(patch), std::domain_error); + CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); + } + } + } + + SECTION("Examples from bruth.github.io/jsonpatch-js") + { + SECTION("add") + { + CHECK(R"( {} )"_json.patch( + R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json + ) == R"( {"foo": "bar"} )"_json); + + CHECK(R"( {"foo": [1, 3]} )"_json.patch( + R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json + ) == R"( {"foo": "bar"} )"_json); + + CHECK(R"( {"foo": [{}]} )"_json.patch( + R"( [{"op": "add", "path": "/foo/0/bar", "value": "baz"}] )"_json + ) == R"( {"foo": [{"bar": "baz"}]} )"_json); + } + + SECTION("remove") + { + CHECK(R"( {"foo": "bar"} )"_json.patch( + R"( [{"op": "remove", "path": "/foo"}] )"_json + ) == R"( {} )"_json); + + CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( + R"( [{"op": "remove", "path": "/foo/1"}] )"_json + ) == R"( {"foo": [1, 3]} )"_json); + + CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch( + R"( [{"op": "remove", "path": "/foo/0/bar"}] )"_json + ) == R"( {"foo": [{}]} )"_json); + } + + SECTION("replace") + { + CHECK(R"( {"foo": "bar"} )"_json.patch( + R"( [{"op": "replace", "path": "/foo", "value": 1}] )"_json + ) == R"( {"foo": 1} )"_json); + + CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( + R"( [{"op": "replace", "path": "/foo/1", "value": 4}] )"_json + ) == R"( {"foo": [1, 4, 3]} )"_json); + + CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch( + R"( [{"op": "replace", "path": "/foo/0/bar", "value": 1}] )"_json + ) == R"( {"foo": [{"bar": 1}]} )"_json); + } + + SECTION("move") + { + CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( + R"( [{"op": "move", "from": "/foo", "path": "/bar"}] )"_json + ) == R"( {"bar": [1, 2, 3]} )"_json); + } + + SECTION("copy") + { + CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( + R"( [{"op": "copy", "from": "/foo/1", "path": "/bar"}] )"_json + ) == R"( {"foo": [1, 2, 3], "bar": 2} )"_json); + } + + SECTION("copy") + { + CHECK_NOTHROW(R"( {"foo": "bar"} )"_json.patch( + R"( [{"op": "test", "path": "/foo", "value": "bar"}] )"_json)); + } + } +} diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp new file mode 100644 index 00000000..b14a4593 --- /dev/null +++ b/test/src/unit-json_pointer.cpp @@ -0,0 +1,388 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("JSON pointers") +{ + SECTION("errors") + { + CHECK_THROWS_AS(json::json_pointer("foo"), std::domain_error); + CHECK_THROWS_WITH(json::json_pointer("foo"), "JSON pointer must be empty or begin with '/'"); + + CHECK_THROWS_AS(json::json_pointer("/~~"), std::domain_error); + CHECK_THROWS_WITH(json::json_pointer("/~~"), "escape error: '~' must be followed with '0' or '1'"); + + CHECK_THROWS_AS(json::json_pointer("/~"), std::domain_error); + CHECK_THROWS_WITH(json::json_pointer("/~"), "escape error: '~' must be followed with '0' or '1'"); + + json::json_pointer p; + CHECK_THROWS_AS(p.top(), std::domain_error); + CHECK_THROWS_WITH(p.top(), "JSON pointer has no parent"); + CHECK_THROWS_AS(p.pop_back(), std::domain_error); + CHECK_THROWS_WITH(p.pop_back(), "JSON pointer has no parent"); + } + + SECTION("examples from RFC 6901") + { + SECTION("nonconst access") + { + json j = R"( + { + "foo": ["bar", "baz"], + "": 0, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + " ": 7, + "m~n": 8 + } + )"_json; + + // the whole document + CHECK(j[json::json_pointer()] == j); + CHECK(j[json::json_pointer("")] == j); + + // array access + CHECK(j[json::json_pointer("/foo")] == j["foo"]); + CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]); + CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]); + CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); + + // checked array access + CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]); + CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]); + + // empty string access + CHECK(j[json::json_pointer("/")] == j[""]); + + // other cases + CHECK(j[json::json_pointer("/ ")] == j[" "]); + CHECK(j[json::json_pointer("/c%d")] == j["c%d"]); + CHECK(j[json::json_pointer("/e^f")] == j["e^f"]); + CHECK(j[json::json_pointer("/g|h")] == j["g|h"]); + CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]); + CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]); + + // checked access + CHECK(j.at(json::json_pointer("/ ")) == j[" "]); + CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]); + CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]); + CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]); + CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]); + CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]); + + // escaped access + CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]); + CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]); + + // unescaped access + CHECK_THROWS_AS(j[json::json_pointer("/a/b")], std::out_of_range); + CHECK_THROWS_WITH(j[json::json_pointer("/a/b")], "unresolved reference token 'b'"); + // "/a/b" works for JSON {"a": {"b": 42}} + CHECK(json({{"a", {{"b", 42}}}})[json::json_pointer("/a/b")] == json(42)); + + // unresolved access + json j_primitive = 1; + CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range); + CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'"); + CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range); + CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'"); + } + + SECTION("const access") + { + const json j = R"( + { + "foo": ["bar", "baz"], + "": 0, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + " ": 7, + "m~n": 8 + } + )"_json; + + // the whole document + CHECK(j[json::json_pointer()] == j); + CHECK(j[json::json_pointer("")] == j); + + // array access + CHECK(j[json::json_pointer("/foo")] == j["foo"]); + CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]); + CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]); + CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); + + // checked array access + CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]); + CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]); + + // empty string access + CHECK(j[json::json_pointer("/")] == j[""]); + + // other cases + CHECK(j[json::json_pointer("/ ")] == j[" "]); + CHECK(j[json::json_pointer("/c%d")] == j["c%d"]); + CHECK(j[json::json_pointer("/e^f")] == j["e^f"]); + CHECK(j[json::json_pointer("/g|h")] == j["g|h"]); + CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]); + CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]); + + // checked access + CHECK(j.at(json::json_pointer("/ ")) == j[" "]); + CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]); + CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]); + CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]); + CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]); + CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]); + + // escaped access + CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]); + CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]); + + // unescaped access + CHECK_THROWS_AS(j.at(json::json_pointer("/a/b")), std::out_of_range); + CHECK_THROWS_WITH(j.at(json::json_pointer("/a/b")), "key 'a' not found"); + + // unresolved access + const json j_primitive = 1; + CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range); + CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'"); + CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range); + CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'"); + } + + SECTION("user-defined string literal") + { + json j = R"( + { + "foo": ["bar", "baz"], + "": 0, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + " ": 7, + "m~n": 8 + } + )"_json; + + // the whole document + CHECK(j[""_json_pointer] == j); + + // array access + CHECK(j["/foo"_json_pointer] == j["foo"]); + CHECK(j["/foo/0"_json_pointer] == j["foo"][0]); + CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); + } + } + + SECTION("array access") + { + SECTION("nonconst access") + { + json j = {1, 2, 3}; + const json j_const = j; + + // check reading access + CHECK(j["/0"_json_pointer] == j[0]); + CHECK(j["/1"_json_pointer] == j[1]); + CHECK(j["/2"_json_pointer] == j[2]); + + // assign to existing index + j["/1"_json_pointer] = 13; + CHECK(j[1] == json(13)); + + // assign to nonexisting index + j["/3"_json_pointer] = 33; + CHECK(j[3] == json(33)); + + // assign to nonexisting index (with gap) + j["/5"_json_pointer] = 55; + CHECK(j == json({1, 13, 3, 33, nullptr, 55})); + + // error with leading 0 + CHECK_THROWS_AS(j["/01"_json_pointer], std::domain_error); + CHECK_THROWS_WITH(j["/01"_json_pointer], "array index must not begin with '0'"); + CHECK_THROWS_AS(j_const["/01"_json_pointer], std::domain_error); + CHECK_THROWS_WITH(j_const["/01"_json_pointer], "array index must not begin with '0'"); + CHECK_THROWS_AS(j.at("/01"_json_pointer), std::domain_error); + CHECK_THROWS_WITH(j.at("/01"_json_pointer), "array index must not begin with '0'"); + CHECK_THROWS_AS(j_const.at("/01"_json_pointer), std::domain_error); + CHECK_THROWS_WITH(j_const.at("/01"_json_pointer), "array index must not begin with '0'"); + + // error with incorrect numbers + CHECK_THROWS_AS(j["/one"_json_pointer] = 1, std::invalid_argument); + + // assign to "-" + j["/-"_json_pointer] = 99; + CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99})); + + // error when using "-" in const object + CHECK_THROWS_AS(j_const["/-"_json_pointer], std::out_of_range); + CHECK_THROWS_WITH(j_const["/-"_json_pointer], "array index '-' (3) is out of range"); + + // error when using "-" with at + CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range); + CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (7) is out of range"); + CHECK_THROWS_AS(j_const.at("/-"_json_pointer), std::out_of_range); + CHECK_THROWS_WITH(j_const.at("/-"_json_pointer), "array index '-' (3) is out of range"); + } + + SECTION("const access") + { + const json j = {1, 2, 3}; + + // check reading access + CHECK(j["/0"_json_pointer] == j[0]); + CHECK(j["/1"_json_pointer] == j[1]); + CHECK(j["/2"_json_pointer] == j[2]); + + // assign to nonexisting index + CHECK_THROWS_AS(j.at("/3"_json_pointer), std::out_of_range); + CHECK_THROWS_WITH(j.at("/3"_json_pointer), "array index 3 is out of range"); + + // assign to nonexisting index (with gap) + CHECK_THROWS_AS(j.at("/5"_json_pointer), std::out_of_range); + CHECK_THROWS_WITH(j.at("/5"_json_pointer), "array index 5 is out of range"); + + // assign to "-" + CHECK_THROWS_AS(j["/-"_json_pointer], std::out_of_range); + CHECK_THROWS_WITH(j["/-"_json_pointer], "array index '-' (3) is out of range"); + CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range); + CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (3) is out of range"); + } + + } + + SECTION("flatten") + { + json j = + { + {"pi", 3.141}, + {"happy", true}, + {"name", "Niels"}, + {"nothing", nullptr}, + { + "answer", { + {"everything", 42} + } + }, + {"list", {1, 0, 2}}, + { + "object", { + {"currency", "USD"}, + {"value", 42.99}, + {"", "empty string"}, + {"/", "slash"}, + {"~", "tilde"}, + {"~1", "tilde1"} + } + } + }; + + json j_flatten = + { + {"/pi", 3.141}, + {"/happy", true}, + {"/name", "Niels"}, + {"/nothing", nullptr}, + {"/answer/everything", 42}, + {"/list/0", 1}, + {"/list/1", 0}, + {"/list/2", 2}, + {"/object/currency", "USD"}, + {"/object/value", 42.99}, + {"/object/", "empty string"}, + {"/object/~1", "slash"}, + {"/object/~0", "tilde"}, + {"/object/~01", "tilde1"} + }; + + // check if flattened result is as expected + CHECK(j.flatten() == j_flatten); + + // check if unflattened result is as expected + CHECK(j_flatten.unflatten() == j); + + // error for nonobjects + CHECK_THROWS_AS(json(1).unflatten(), std::domain_error); + CHECK_THROWS_WITH(json(1).unflatten(), "only objects can be unflattened"); + + // error for nonprimitve values + CHECK_THROWS_AS(json({{"/1", {1, 2, 3}}}).unflatten(), std::domain_error); + CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(), "values in object must be primitive"); + + // error for conflicting values + json j_error = {{"", 42}, {"/foo", 17}}; + CHECK_THROWS_AS(j_error.unflatten(), std::domain_error); + CHECK_THROWS_WITH(j_error.unflatten(), "invalid value to unflatten"); + + // explicit roundtrip check + CHECK(j.flatten().unflatten() == j); + + // roundtrip for primitive values + json j_null; + CHECK(j_null.flatten().unflatten() == j_null); + json j_number = 42; + CHECK(j_number.flatten().unflatten() == j_number); + json j_boolean = false; + CHECK(j_boolean.flatten().unflatten() == j_boolean); + json j_string = "foo"; + CHECK(j_string.flatten().unflatten() == j_string); + + // roundtrip for empty structured values (will be unflattened to null) + json j_array(json::value_t::array); + CHECK(j_array.flatten().unflatten() == json()); + json j_object(json::value_t::object); + CHECK(j_object.flatten().unflatten() == json()); + } + + SECTION("string representation") + { + for (auto ptr : + {"", "/foo", "/foo/0", "/", "/a~1b", "/c%d", "/e^f", "/g|h", "/i\\j", "/k\"l", "/ ", "/m~0n" + }) + { + CHECK(json::json_pointer(ptr).to_string() == ptr); + } + } +} diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp new file mode 100644 index 00000000..9cb33684 --- /dev/null +++ b/test/src/unit-modifiers.cpp @@ -0,0 +1,713 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("modifiers") +{ + SECTION("clear()") + { + SECTION("boolean") + { + json j = true; + + j.clear(); + CHECK(j == json(json::value_t::boolean)); + } + + SECTION("string") + { + json j = "hello world"; + + j.clear(); + CHECK(j == json(json::value_t::string)); + } + + SECTION("array") + { + SECTION("empty array") + { + json j = json::array(); + + j.clear(); + CHECK(j.empty()); + CHECK(j == json(json::value_t::array)); + } + + SECTION("filled array") + { + json j = {1, 2, 3}; + + j.clear(); + CHECK(j.empty()); + CHECK(j == json(json::value_t::array)); + } + } + + SECTION("object") + { + SECTION("empty object") + { + json j = json::object(); + + j.clear(); + CHECK(j.empty()); + CHECK(j == json(json::value_t::object)); + } + + SECTION("filled object") + { + json j = {{"one", 1}, {"two", 2}, {"three", 3}}; + + j.clear(); + CHECK(j.empty()); + CHECK(j == json(json::value_t::object)); + } + } + + SECTION("number (integer)") + { + json j = 23; + + j.clear(); + CHECK(j == json(json::value_t::number_integer)); + } + + SECTION("number (unsigned)") + { + json j = 23u; + + j.clear(); + CHECK(j == json(json::value_t::number_integer)); + } + + SECTION("number (float)") + { + json j = 23.42; + + j.clear(); + CHECK(j == json(json::value_t::number_float)); + } + + SECTION("null") + { + json j = nullptr; + + j.clear(); + CHECK(j == json(json::value_t::null)); + } + } + + SECTION("push_back()") + { + SECTION("to array") + { + SECTION("json&&") + { + SECTION("null") + { + json j; + j.push_back(1); + j.push_back(2); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2})); + } + + SECTION("array") + { + json j = {1, 2, 3}; + j.push_back("Hello"); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2, 3, "Hello"})); + } + + SECTION("other type") + { + json j = 1; + CHECK_THROWS_AS(j.push_back("Hello"), std::domain_error); + CHECK_THROWS_WITH(j.push_back("Hello"), "cannot use push_back() with number"); + } + } + + SECTION("const json&") + { + SECTION("null") + { + json j; + json k(1); + j.push_back(k); + j.push_back(k); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 1})); + } + + SECTION("array") + { + json j = {1, 2, 3}; + json k("Hello"); + j.push_back(k); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2, 3, "Hello"})); + } + + SECTION("other type") + { + json j = 1; + json k("Hello"); + CHECK_THROWS_AS(j.push_back(k), std::domain_error); + CHECK_THROWS_WITH(j.push_back(k), "cannot use push_back() with number"); + } + } + } + + SECTION("to object") + { + SECTION("null") + { + json j; + j.push_back(json::object_t::value_type({"one", 1})); + j.push_back(json::object_t::value_type({"two", 2})); + CHECK(j.type() == json::value_t::object); + CHECK(j.size() == 2); + CHECK(j["one"] == json(1)); + CHECK(j["two"] == json(2)); + } + + SECTION("object") + { + json j(json::value_t::object); + j.push_back(json::object_t::value_type({"one", 1})); + j.push_back(json::object_t::value_type({"two", 2})); + CHECK(j.size() == 2); + CHECK(j["one"] == json(1)); + CHECK(j["two"] == json(2)); + } + + SECTION("other type") + { + json j = 1; + json k("Hello"); + CHECK_THROWS_AS(j.push_back(json::object_t::value_type({"one", 1})), std::domain_error); + CHECK_THROWS_WITH(j.push_back(json::object_t::value_type({"one", 1})), + "cannot use push_back() with number"); + } + } + + SECTION("with initializer_list") + { + SECTION("null") + { + json j; + j.push_back({"foo", "bar"}); + CHECK(j == json::array({{"foo", "bar"}})); + + json k; + k.push_back({1, 2, 3}); + CHECK(k == json::array({{1, 2, 3}})); + } + + SECTION("array") + { + json j = {1, 2, 3}; + j.push_back({"foo", "bar"}); + CHECK(j == json({1, 2, 3, {"foo", "bar"}})); + + json k = {1, 2, 3}; + k.push_back({1, 2, 3}); + CHECK(k == json({1, 2, 3, {1, 2, 3}})); + } + + SECTION("object") + { + json j = {{"key1", 1}}; + j.push_back({"key2", "bar"}); + CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); + + json k = {{"key1", 1}}; + CHECK_THROWS_AS(k.push_back({1, 2, 3, 4}), std::domain_error); + CHECK_THROWS_WITH(k.push_back({1, 2, 3, 4}), "cannot use push_back() with object"); + } + } + } + + SECTION("operator+=") + { + SECTION("to array") + { + SECTION("json&&") + { + SECTION("null") + { + json j; + j += 1; + j += 2; + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2})); + } + + SECTION("array") + { + json j = {1, 2, 3}; + j += "Hello"; + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2, 3, "Hello"})); + } + + SECTION("other type") + { + json j = 1; + CHECK_THROWS_AS(j += "Hello", std::domain_error); + CHECK_THROWS_WITH(j += "Hello", "cannot use push_back() with number"); + } + } + + SECTION("const json&") + { + SECTION("null") + { + json j; + json k(1); + j += k; + j += k; + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 1})); + } + + SECTION("array") + { + json j = {1, 2, 3}; + json k("Hello"); + j += k; + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2, 3, "Hello"})); + } + + SECTION("other type") + { + json j = 1; + json k("Hello"); + CHECK_THROWS_AS(j += k, std::domain_error); + CHECK_THROWS_WITH(j += k, "cannot use push_back() with number"); + } + } + } + + SECTION("to object") + { + SECTION("null") + { + json j; + j += json::object_t::value_type({"one", 1}); + j += json::object_t::value_type({"two", 2}); + CHECK(j.type() == json::value_t::object); + CHECK(j.size() == 2); + CHECK(j["one"] == json(1)); + CHECK(j["two"] == json(2)); + } + + SECTION("object") + { + json j(json::value_t::object); + j += json::object_t::value_type({"one", 1}); + j += json::object_t::value_type({"two", 2}); + CHECK(j.size() == 2); + CHECK(j["one"] == json(1)); + CHECK(j["two"] == json(2)); + } + + SECTION("other type") + { + json j = 1; + json k("Hello"); + CHECK_THROWS_AS(j += json::object_t::value_type({"one", 1}), std::domain_error); + CHECK_THROWS_WITH(j += json::object_t::value_type({"one", 1}), + "cannot use push_back() with number"); + } + } + + SECTION("with initializer_list") + { + SECTION("null") + { + json j; + j += {"foo", "bar"}; + CHECK(j == json::array({{"foo", "bar"}})); + + json k; + k += {1, 2, 3}; + CHECK(k == json::array({{1, 2, 3}})); + } + + SECTION("array") + { + json j = {1, 2, 3}; + j += {"foo", "bar"}; + CHECK(j == json({1, 2, 3, {"foo", "bar"}})); + + json k = {1, 2, 3}; + k += {1, 2, 3}; + CHECK(k == json({1, 2, 3, {1, 2, 3}})); + } + + SECTION("object") + { + json j = {{"key1", 1}}; + j += {"key2", "bar"}; + CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); + + json k = {{"key1", 1}}; + CHECK_THROWS_AS((k += {1, 2, 3, 4}), std::domain_error); + CHECK_THROWS_WITH((k += {1, 2, 3, 4}), "cannot use push_back() with object"); + } + } + } + + SECTION("insert") + { + json j_array = {1, 2, 3, 4}; + json j_value = 5; + + SECTION("value at position") + { + SECTION("insert before begin()") + { + auto it = j_array.insert(j_array.begin(), j_value); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK(j_array.begin() == it); + CHECK(j_array == json({5, 1, 2, 3, 4})); + } + + SECTION("insert in the middle") + { + auto it = j_array.insert(j_array.begin() + 2, j_value); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK((it - j_array.begin()) == 2); + CHECK(j_array == json({1, 2, 5, 3, 4})); + } + + SECTION("insert before end()") + { + auto it = j_array.insert(j_array.end(), j_value); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK((j_array.end() - it) == 1); + CHECK(j_array == json({1, 2, 3, 4, 5})); + } + } + + SECTION("rvalue at position") + { + SECTION("insert before begin()") + { + auto it = j_array.insert(j_array.begin(), 5); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK(j_array.begin() == it); + CHECK(j_array == json({5, 1, 2, 3, 4})); + } + + SECTION("insert in the middle") + { + auto it = j_array.insert(j_array.begin() + 2, 5); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK((it - j_array.begin()) == 2); + CHECK(j_array == json({1, 2, 5, 3, 4})); + } + + SECTION("insert before end()") + { + auto it = j_array.insert(j_array.end(), 5); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK((j_array.end() - it) == 1); + CHECK(j_array == json({1, 2, 3, 4, 5})); + } + } + + SECTION("copies at position") + { + SECTION("insert before begin()") + { + auto it = j_array.insert(j_array.begin(), 3, 5); + CHECK(j_array.size() == 7); + CHECK(*it == j_value); + CHECK(j_array.begin() == it); + CHECK(j_array == json({5, 5, 5, 1, 2, 3, 4})); + } + + SECTION("insert in the middle") + { + auto it = j_array.insert(j_array.begin() + 2, 3, 5); + CHECK(j_array.size() == 7); + CHECK(*it == j_value); + CHECK((it - j_array.begin()) == 2); + CHECK(j_array == json({1, 2, 5, 5, 5, 3, 4})); + } + + SECTION("insert before end()") + { + auto it = j_array.insert(j_array.end(), 3, 5); + CHECK(j_array.size() == 7); + CHECK(*it == j_value); + CHECK((j_array.end() - it) == 3); + CHECK(j_array == json({1, 2, 3, 4, 5, 5, 5})); + } + + SECTION("insert nothing (count = 0)") + { + auto pos = j_array.end(); + auto it = j_array.insert(j_array.end(), 0, 5); + CHECK(j_array.size() == 4); + CHECK(it == pos); + CHECK(j_array == json({1, 2, 3, 4})); + } + } + + SECTION("range") + { + json j_other_array = {"first", "second"}; + + SECTION("proper usage") + { + auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.end()); + CHECK(j_array.size() == 6); + CHECK(*it == *j_other_array.begin()); + CHECK((j_array.end() - it) == 2); + CHECK(j_array == json({1, 2, 3, 4, "first", "second"})); + } + + SECTION("empty range") + { + auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.begin()); + CHECK(j_array.size() == 4); + CHECK(it == j_array.end()); + CHECK(j_array == json({1, 2, 3, 4})); + } + + SECTION("invalid iterators") + { + json j_other_array2 = {"first", "second"}; + + CHECK_THROWS_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), + std::domain_error); + + CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), + "passed iterators may not belong to container"); + CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), + "iterators do not fit"); + } + } + + SECTION("initializer list at position") + { + SECTION("insert before begin()") + { + auto it = j_array.insert(j_array.begin(), {7, 8, 9}); + CHECK(j_array.size() == 7); + CHECK(*it == json(7)); + CHECK(j_array.begin() == it); + CHECK(j_array == json({7, 8, 9, 1, 2, 3, 4})); + } + + SECTION("insert in the middle") + { + auto it = j_array.insert(j_array.begin() + 2, {7, 8, 9}); + CHECK(j_array.size() == 7); + CHECK(*it == json(7)); + CHECK((it - j_array.begin()) == 2); + CHECK(j_array == json({1, 2, 7, 8, 9, 3, 4})); + } + + SECTION("insert before end()") + { + auto it = j_array.insert(j_array.end(), {7, 8, 9}); + CHECK(j_array.size() == 7); + CHECK(*it == json(7)); + CHECK((j_array.end() - it) == 3); + CHECK(j_array == json({1, 2, 3, 4, 7, 8, 9})); + } + } + + SECTION("invalid iterator") + { + // pass iterator to a different array + json j_another_array = {1, 2}; + json j_yet_another_array = {"first", "second"}; + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_value), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10, 11), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), + j_yet_another_array.end()), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), std::domain_error); + + CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10), "iterator does not fit current value"); + CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_value), + "iterator does not fit current value"); + CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10, 11), + "iterator does not fit current value"); + CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), + j_yet_another_array.end()), "iterator does not fit current value"); + CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), + "iterator does not fit current value"); + } + + SECTION("non-array type") + { + // call insert on a non-array type + json j_nonarray = 3; + json j_yet_another_array = {"first", "second"}; + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10), std::domain_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_value), std::domain_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), std::domain_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), + j_yet_another_array.end()), std::domain_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10), "cannot use insert() with number"); + CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_value), "cannot use insert() with number"); + CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10, 11), "cannot use insert() with number"); + CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), + j_yet_another_array.end()), "cannot use insert() with number"); + CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), + "cannot use insert() with number"); + } + } + + SECTION("swap()") + { + SECTION("json") + { + SECTION("member swap") + { + json j("hello world"); + json k(42.23); + + j.swap(k); + + CHECK(j == json(42.23)); + CHECK(k == json("hello world")); + } + + SECTION("nonmember swap") + { + json j("hello world"); + json k(42.23); + + std::swap(j, k); + + CHECK(j == json(42.23)); + CHECK(k == json("hello world")); + } + } + + SECTION("array_t") + { + SECTION("array_t type") + { + json j = {1, 2, 3, 4}; + json::array_t a = {"foo", "bar", "baz"}; + + j.swap(a); + + CHECK(j == json({"foo", "bar", "baz"})); + + j.swap(a); + + CHECK(j == json({1, 2, 3, 4})); + } + + SECTION("non-array_t type") + { + json j = 17; + json::array_t a = {"foo", "bar", "baz"}; + + CHECK_THROWS_AS(j.swap(a), std::domain_error); + CHECK_THROWS_WITH(j.swap(a), "cannot use swap() with number"); + } + } + + SECTION("object_t") + { + SECTION("object_t type") + { + json j = {{"one", 1}, {"two", 2}}; + json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}}; + + j.swap(o); + + CHECK(j == json({{"cow", "Kuh"}, {"chicken", "Huhn"}})); + + j.swap(o); + + CHECK(j == json({{"one", 1}, {"two", 2}})); + } + + SECTION("non-object_t type") + { + json j = 17; + json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}}; + + CHECK_THROWS_AS(j.swap(o), std::domain_error); + CHECK_THROWS_WITH(j.swap(o), "cannot use swap() with number"); + } + } + + SECTION("string_t") + { + SECTION("string_t type") + { + json j = "Hello world"; + json::string_t s = "Hallo Welt"; + + j.swap(s); + + CHECK(j == json("Hallo Welt")); + + j.swap(s); + + CHECK(j == json("Hello world")); + } + + SECTION("non-string_t type") + { + json j = 17; + json::string_t s = "Hallo Welt"; + + CHECK_THROWS_AS(j.swap(s), std::domain_error); + CHECK_THROWS_WITH(j.swap(s), "cannot use swap() with number"); + } + } + } +} diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp new file mode 100644 index 00000000..bae9ee4c --- /dev/null +++ b/test/src/unit-pointer_access.cpp @@ -0,0 +1,263 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("pointer access") +{ + // create a JSON value with different types + json json_types = + { + {"boolean", true}, + { + "number", { + {"integer", 42}, + {"unsigned", 42u}, + {"floating-point", 17.23} + } + }, + {"string", "Hello, world!"}, + {"array", {1, 2, 3, 4, 5}}, + {"null", nullptr} + }; + + SECTION("pointer access to object_t") + { + using test_type = json::object_t; + json value = {{"one", 1}, {"two", 2}}; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to const object_t") + { + using test_type = json::object_t; + const json value = {{"one", 1}, {"two", 2}}; + + // this should not compile + // test_type* p1 = value.get_ptr(); + + // check if pointers are returned correctly + const test_type* p2 = value.get_ptr(); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p2 == p3); + } + + SECTION("pointer access to array_t") + { + using test_type = json::array_t; + json value = {1, 2, 3, 4}; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to string_t") + { + using test_type = json::string_t; + json value = "hello"; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to boolean_t") + { + using test_type = json::boolean_t; + json value = false; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to number_integer_t") + { + using test_type = json::number_integer_t; + json value = 23; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to number_unsigned_t") + { + using test_type = json::number_unsigned_t; + json value = 23u; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to number_float_t") + { + using test_type = json::number_float_t; + json value = 42.23; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == Approx(value.get())); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == Approx(value.get())); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == Approx(value.get())); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + } +} diff --git a/test/src/unit-readme.cpp b/test/src/unit-readme.cpp new file mode 100644 index 00000000..7e7ef6b2 --- /dev/null +++ b/test/src/unit-readme.cpp @@ -0,0 +1,299 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +#include +#include +#include +#include +#include + +TEST_CASE("README", "[hide]") +{ + { + // redirect std::cout for the README file + auto old_cout_buffer = std::cout.rdbuf(); + std::ostringstream new_stream; + std::cout.rdbuf(new_stream.rdbuf()); + { + // create an empty structure (null) + json j; + + // add a number that is stored as double (note the implicit conversion of j to an object) + j["pi"] = 3.141; + + // add a Boolean that is stored as bool + j["happy"] = true; + + // add a string that is stored as std::string + j["name"] = "Niels"; + + // add another null object by passing nullptr + j["nothing"] = nullptr; + + // add an object inside the object + j["answer"]["everything"] = 42; + + // add an array that is stored as std::vector (using an initializer list) + j["list"] = { 1, 0, 2 }; + + // add another object (using an initializer list of pairs) + j["object"] = { {"currency", "USD"}, {"value", 42.99} }; + + // instead, you could also write (which looks very similar to the JSON above) + json j2 = + { + {"pi", 3.141}, + {"happy", true}, + {"name", "Niels"}, + {"nothing", nullptr}, + { + "answer", { + {"everything", 42} + } + }, + {"list", {1, 0, 2}}, + { + "object", { + {"currency", "USD"}, + {"value", 42.99} + } + } + }; + } + + { + // ways to express the empty array [] + json empty_array_implicit = {{}}; + json empty_array_explicit = json::array(); + + // a way to express the empty object {} + json empty_object_explicit = json::object(); + + // a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]] + json array_not_object = { json::array({"currency", "USD"}), json::array({"value", 42.99}) }; + } + + { + // create object from string literal + json j = "{ \"happy\": true, \"pi\": 3.141 }"_json; + + // or even nicer with a raw string literal + auto j2 = R"( + { + "happy": true, + "pi": 3.141 + } + )"_json; + + // or explicitly + auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }"); + + // explicit conversion to string + std::string s = j.dump(); // {\"happy\":true,\"pi\":3.141} + + // serialization with pretty printing + // pass in the amount of spaces to indent + std::cout << j.dump(4) << std::endl; + // { + // "happy": true, + // "pi": 3.141 + // } + + std::cout << std::setw(2) << j << std::endl; + } + + { + // create an array using push_back + json j; + j.push_back("foo"); + j.push_back(1); + j.push_back(true); + + // iterate the array + for (json::iterator it = j.begin(); it != j.end(); ++it) + { + std::cout << *it << '\n'; + } + + // range-based for + for (auto element : j) + { + std::cout << element << '\n'; + } + + // getter/setter + const std::string tmp = j[0]; + j[1] = 42; + bool foo = j.at(2); + + // other stuff + j.size(); // 3 entries + j.empty(); // false + j.type(); // json::value_t::array + j.clear(); // the array is empty again + + // comparison + j == "[\"foo\", 1, true]"_json; // true + + // create an object + json o; + o["foo"] = 23; + o["bar"] = false; + o["baz"] = 3.141; + + // find an entry + if (o.find("foo") != o.end()) + { + // there is an entry with key "foo" + } + } + + { + std::vector c_vector {1, 2, 3, 4}; + json j_vec(c_vector); + // [1, 2, 3, 4] + + std::deque c_deque {1.2f, 2.3f, 3.4f, 5.6f}; + json j_deque(c_deque); + // [1.2, 2.3, 3.4, 5.6] + + std::list c_list {true, true, false, true}; + json j_list(c_list); + // [true, true, false, true] + + std::forward_list c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543}; + json j_flist(c_flist); + // [12345678909876, 23456789098765, 34567890987654, 45678909876543] + + std::array c_array {{1, 2, 3, 4}}; + json j_array(c_array); + // [1, 2, 3, 4] + + std::set c_set {"one", "two", "three", "four", "one"}; + json j_set(c_set); // only one entry for "one" is used + // ["four", "one", "three", "two"] + + std::unordered_set c_uset {"one", "two", "three", "four", "one"}; + json j_uset(c_uset); // only one entry for "one" is used + // maybe ["two", "three", "four", "one"] + + std::multiset c_mset {"one", "two", "one", "four"}; + json j_mset(c_mset); // only one entry for "one" is used + // maybe ["one", "two", "four"] + + std::unordered_multiset c_umset {"one", "two", "one", "four"}; + json j_umset(c_umset); // both entries for "one" are used + // maybe ["one", "two", "one", "four"] + } + + { + std::map c_map { {"one", 1}, {"two", 2}, {"three", 3} }; + json j_map(c_map); + // {"one": 1, "two": 2, "three": 3} + + std::unordered_map c_umap { {"one", 1.2f}, {"two", 2.3f}, {"three", 3.4f} }; + json j_umap(c_umap); + // {"one": 1.2, "two": 2.3, "three": 3.4} + + std::multimap c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; + json j_mmap(c_mmap); // only one entry for key "three" is used + // maybe {"one": true, "two": true, "three": true} + + std::unordered_multimap c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; + json j_ummap(c_ummap); // only one entry for key "three" is used + // maybe {"one": true, "two": true, "three": true} + } + + { + // strings + std::string s1 = "Hello, world!"; + json js = s1; + std::string s2 = js; + + // Booleans + bool b1 = true; + json jb = b1; + bool b2 = jb; + + // numbers + int i = 42; + json jn = i; + double f = jn; + + // etc. + + std::string vs = js.get(); + bool vb = jb.get(); + int vi = jn.get(); + + // etc. + } + + { + // a JSON value + json j_original = R"({ + "baz": ["one", "two", "three"], + "foo": "bar" + })"_json; + + // access members with a JSON pointer (RFC 6901) + j_original["/baz/1"_json_pointer]; + // "two" + + // a JSON patch (RFC 6902) + json j_patch = R"([ + { "op": "replace", "path": "/baz", "value": "boo" }, + { "op": "add", "path": "/hello", "value": ["world"] }, + { "op": "remove", "path": "/foo"} + ])"_json; + + // apply the patch + json j_result = j_original.patch(j_patch); + // { + // "baz": "boo", + // "hello": ["world"] + // } + + // calculate a JSON patch from two JSON values + json::diff(j_result, j_original); + // [ + // { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] }, + // { "op":"remove","path":"/hello" }, + // { "op":"add","path":"/foo","value":"bar" } + // ] + } + + // restore old std::cout + std::cout.rdbuf(old_cout_buffer); + } +} diff --git a/test/src/unit-reference_access.cpp b/test/src/unit-reference_access.cpp new file mode 100644 index 00000000..922c4825 --- /dev/null +++ b/test/src/unit-reference_access.cpp @@ -0,0 +1,202 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("reference access") +{ + // create a JSON value with different types + json json_types = + { + {"boolean", true}, + { + "number", { + {"integer", 42}, + {"floating-point", 17.23} + } + }, + {"string", "Hello, world!"}, + {"array", {1, 2, 3, 4, 5}}, + {"null", nullptr} + }; + + SECTION("reference access to object_t") + { + using test_type = json::object_t; + json value = {{"one", 1}, {"two", 2}}; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("const reference access to const object_t") + { + using test_type = json::object_t; + const json value = {{"one", 1}, {"two", 2}}; + + // this should not compile + // test_type& p1 = value.get_ref(); + + // check if references are returned correctly + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + } + + SECTION("reference access to array_t") + { + using test_type = json::array_t; + json value = {1, 2, 3, 4}; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("reference access to string_t") + { + using test_type = json::string_t; + json value = "hello"; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("reference access to boolean_t") + { + using test_type = json::boolean_t; + json value = false; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("reference access to number_integer_t") + { + using test_type = json::number_integer_t; + json value = 23; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("reference access to number_float_t") + { + using test_type = json::number_float_t; + json value = 42.23; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + } +} diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp new file mode 100644 index 00000000..da4c3d23 --- /dev/null +++ b/test/src/unit-regression.cpp @@ -0,0 +1,443 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("regression tests") +{ + SECTION("issue #60 - Double quotation mark is not parsed correctly") + { + SECTION("escape_dobulequote") + { + auto s = "[\"\\\"foo\\\"\"]"; + json j = json::parse(s); + auto expected = R"(["\"foo\""])"_json; + CHECK(j == expected); + } + } + + SECTION("issue #70 - Handle infinity and NaN cases") + { + SECTION("NAN value") + { + CHECK(json(NAN) == json()); + CHECK(json(json::number_float_t(NAN)) == json()); + } + + SECTION("infinity") + { + CHECK(json(INFINITY) == json()); + CHECK(json(json::number_float_t(INFINITY)) == json()); + } + } + + SECTION("pull request #71 - handle enum type") + { + enum { t = 0 }; + json j = json::array(); + j.push_back(t); + + j.push_back(json::object( + { + {"game_type", t} + })); + } + + SECTION("issue #76 - dump() / parse() not idempotent") + { + // create JSON object + json fields; + fields["one"] = std::string("one"); + fields["two"] = std::string("two three"); + fields["three"] = std::string("three \"four\""); + + // create another JSON object by deserializing the serialization + std::string payload = fields.dump(); + json parsed_fields = json::parse(payload); + + // check individual fields to match both objects + CHECK(parsed_fields["one"] == fields["one"]); + CHECK(parsed_fields["two"] == fields["two"]); + CHECK(parsed_fields["three"] == fields["three"]); + + // check individual fields to match original input + CHECK(parsed_fields["one"] == std::string("one")); + CHECK(parsed_fields["two"] == std::string("two three")); + CHECK(parsed_fields["three"] == std::string("three \"four\"")); + + // check equality of the objects + CHECK(parsed_fields == fields); + + // check equality of the serialized objects + CHECK(fields.dump() == parsed_fields.dump()); + + // check everything in one line + CHECK(fields == json::parse(fields.dump())); + } + + SECTION("issue #82 - lexer::get_number return NAN") + { + const auto content = R"( + { + "Test":"Test1", + "Number":100, + "Foo":42.42 + })"; + + std::stringstream ss; + ss << content; + json j; + ss >> j; + + std::string test = j["Test"]; + CHECK(test == "Test1"); + int number = j["Number"]; + CHECK(number == 100); + float foo = j["Foo"]; + CHECK(foo == Approx(42.42)); + } + + SECTION("issue #89 - nonstandard integer type") + { + // create JSON class with nonstandard integer number type + using custom_json = + nlohmann::basic_json; + custom_json j; + j["int_1"] = 1; + // we need to cast to int to compile with Catch - the value is int32_t + CHECK(static_cast(j["int_1"]) == 1); + + // tests for correct handling of non-standard integers that overflow the type selected by the user + + // unsigned integer object creation - expected to wrap and still be stored as an integer + j = 4294967296U; // 2^32 + CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_unsigned)); + CHECK(j.get() == 0); // Wrap + + // unsigned integer parsing - expected to overflow and be stored as a float + j = custom_json::parse("4294967296"); // 2^32 + CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_float)); + CHECK(j.get() == 4294967296.0f); + + // integer object creation - expected to wrap and still be stored as an integer + j = -2147483649LL; // -2^31-1 + CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_integer)); + CHECK(j.get() == 2147483647); // Wrap + + // integer parsing - expected to overflow and be stored as a float with rounding + j = custom_json::parse("-2147483649"); // -2^31 + CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_float)); + CHECK(j.get() == -2147483650.0f); + } + + SECTION("issue #93 reverse_iterator operator inheritance problem") + { + { + json a = {1, 2, 3}; + json::reverse_iterator rit = a.rbegin(); + ++rit; + CHECK(*rit == json(2)); + CHECK(rit.value() == json(2)); + } + { + json a = {1, 2, 3}; + json::reverse_iterator rit = ++a.rbegin(); + } + { + json a = {1, 2, 3}; + json::reverse_iterator rit = a.rbegin(); + ++rit; + json b = {0, 0, 0}; + std::transform(rit, a.rend(), b.rbegin(), [](json el) + { + return el; + }); + CHECK(b == json({0, 1, 2})); + } + { + json a = {1, 2, 3}; + json b = {0, 0, 0}; + std::transform(++a.rbegin(), a.rend(), b.rbegin(), [](json el) + { + return el; + }); + CHECK(b == json({0, 1, 2})); + } + } + + SECTION("issue #100 - failed to iterator json object with reverse_iterator") + { + json config = + { + { "111", 111 }, + { "112", 112 }, + { "113", 113 } + }; + + std::stringstream ss; + + for (auto it = config.begin(); it != config.end(); ++it) + { + ss << it.key() << ": " << it.value() << '\n'; + } + + for (auto it = config.rbegin(); it != config.rend(); ++it) + { + ss << it.key() << ": " << it.value() << '\n'; + } + + CHECK(ss.str() == "111: 111\n112: 112\n113: 113\n113: 113\n112: 112\n111: 111\n"); + } + + SECTION("issue #101 - binary string causes numbers to be dumped as hex") + { + int64_t number = 10; + std::string bytes{"\x00" "asdf\n", 6}; + json j; + j["int64"] = number; + j["binary string"] = bytes; + // make sure the number is really printed as decimal "10" and not as + // hexadecimal "a" + CHECK(j.dump() == "{\"binary string\":\"\\u0000asdf\\n\",\"int64\":10}"); + } + + SECTION("issue #111 - subsequent unicode chars") + { + std::string bytes{0x7, 0x7}; + json j; + j["string"] = bytes; + CHECK(j["string"] == "\u0007\u0007"); + } + + SECTION("issue #144 - implicit assignment to std::string fails") + { + json o = {{"name", "value"}}; + + std::string s1 = o["name"]; + CHECK(s1 == "value"); + + std::string s2; + s2 = o["name"]; + + CHECK(s2 == "value"); + } + + SECTION("issue #146 - character following a surrogate pair is skipped") + { + CHECK(json::parse("\"\\ud80c\\udc60abc\"").get() == u8"\U00013060abc"); + } + + SECTION("issue #171 - Cannot index by key of type static constexpr const char*") + { + json j; + + // Non-const access with key as "char []" + char array_key[] = "Key1"; + CHECK_NOTHROW(j[array_key] = 1); + CHECK(j[array_key] == json(1)); + + // Non-const access with key as "const char[]" + const char const_array_key[] = "Key2"; + CHECK_NOTHROW(j[const_array_key] = 2); + CHECK(j[const_array_key] == json(2)); + + // Non-const access with key as "char *" + char _ptr_key[] = "Key3"; + char* ptr_key = &_ptr_key[0]; + CHECK_NOTHROW(j[ptr_key] = 3); + CHECK(j[ptr_key] == json(3)); + + // Non-const access with key as "const char *" + const char* const_ptr_key = "Key4"; + CHECK_NOTHROW(j[const_ptr_key] = 4); + CHECK(j[const_ptr_key] == json(4)); + + // Non-const access with key as "static constexpr const char *" + static constexpr const char* constexpr_ptr_key = "Key5"; + CHECK_NOTHROW(j[constexpr_ptr_key] = 5); + CHECK(j[constexpr_ptr_key] == json(5)); + + const json j_const = j; + + // Const access with key as "char []" + CHECK(j_const[array_key] == json(1)); + + // Const access with key as "const char[]" + CHECK(j_const[const_array_key] == json(2)); + + // Const access with key as "char *" + CHECK(j_const[ptr_key] == json(3)); + + // Const access with key as "const char *" + CHECK(j_const[const_ptr_key] == json(4)); + + // Const access with key as "static constexpr const char *" + CHECK(j_const[constexpr_ptr_key] == json(5)); + } + + SECTION("issue #186 miloyip/nativejson-benchmark: floating-point parsing") + { + json j; + + j = json::parse("-0.0"); + CHECK(j.get() == -0.0); + + j = json::parse("2.22507385850720113605740979670913197593481954635164564e-308"); + CHECK(j.get() == 2.2250738585072009e-308); + + j = json::parse("0.999999999999999944488848768742172978818416595458984374"); + CHECK(j.get() == 0.99999999999999989); + + j = json::parse("1.00000000000000011102230246251565404236316680908203126"); + CHECK(j.get() == 1.00000000000000022); + + j = json::parse("7205759403792793199999e-5"); + CHECK(j.get() == 72057594037927928.0); + + j = json::parse("922337203685477529599999e-5"); + CHECK(j.get() == 9223372036854774784.0); + + j = json::parse("1014120480182583464902367222169599999e-5"); + CHECK(j.get() == 10141204801825834086073718800384.0); + + j = json::parse("5708990770823839207320493820740630171355185151999e-3"); + CHECK(j.get() == 5708990770823838890407843763683279797179383808.0); + + // create JSON class with nonstandard float number type + + // float + nlohmann::basic_json j_float = + 1.23e25f; + CHECK(j_float.get() == 1.23e25f); + + // double + nlohmann::basic_json j_double = + 1.23e35f; + CHECK(j_double.get() == 1.23e35f); + + // long double + nlohmann::basic_json + j_long_double = 1.23e45L; + CHECK(j_long_double.get() == 1.23e45L); + } + + SECTION("issue #228 - double values are serialized with commas as decimal points") + { + json j1a = 23.42; + json j1b = json::parse("23.42"); + + json j2a = 2342e-2; + //issue #230 + //json j2b = json::parse("2342e-2"); + + json j3a = 10E3; + json j3b = json::parse("10E3"); + json j3c = json::parse("10e3"); + + // class to create a locale that would use a comma for decimals + class CommaDecimalSeparator : public std::numpunct + { + protected: + char do_decimal_point() const + { + return ','; + } + }; + + // change locale to mess with decimal points + std::locale::global(std::locale(std::locale(), new CommaDecimalSeparator)); + + CHECK(j1a.dump() == "23.42"); + CHECK(j1b.dump() == "23.42"); + + // check if locale is properly reset + std::stringstream ss; + ss.imbue(std::locale(std::locale(), new CommaDecimalSeparator)); + ss << 47.11; + CHECK(ss.str() == "47,11"); + ss << j1a; + CHECK(ss.str() == "47,1123.42"); + ss << 47.11; + CHECK(ss.str() == "47,1123.4247,11"); + + CHECK(j2a.dump() == "23.42"); + //issue #230 + //CHECK(j2b.dump() == "23.42"); + + CHECK(j3a.dump() == "10000"); + CHECK(j3b.dump() == "10000"); + CHECK(j3c.dump() == "10000"); + //CHECK(j3b.dump() == "1E04"); // roundtrip error + //CHECK(j3c.dump() == "1e04"); // roundtrip error + } + + SECTION("issue #233 - Can't use basic_json::iterator as a base iterator for std::move_iterator") + { + json source = {"a", "b", "c"}; + json expected = {"a", "b"}; + json dest; + + std::copy_n(std::make_move_iterator(source.begin()), 2, std::back_inserter(dest)); + + CHECK(dest == expected); + } + + SECTION("issue #235 - ambiguous overload for 'push_back' and 'operator+='") + { + json data = {{"key", "value"}}; + data.push_back({"key2", "value2"}); + data += {"key3", "value3"}; + + CHECK(data == json({{"key", "value"}, {"key2", "value2"}, {"key3", "value3"}})); + } + + SECTION("issue #269 - diff generates incorrect patch when removing multiple array elements") + { + json doc = R"( { "arr1": [1, 2, 3, 4] } )"_json; + json expected = R"( { "arr1": [1, 2] } )"_json; + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("issue #283 - value() does not work with _json_pointer types") + { + json j = + { + {"object", {{"key1", 1}, {"key2", 2}}}, + }; + + int at_integer = j.at("/object/key2"_json_pointer); + int val_integer = j.value("/object/key2"_json_pointer, 0); + + CHECK(at_integer == val_integer); + } +} diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp new file mode 100644 index 00000000..13333ff2 --- /dev/null +++ b/test/src/unit-serialization.cpp @@ -0,0 +1,76 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("serialization") +{ + SECTION("operator<<") + { + SECTION("no given width") + { + std::stringstream ss; + json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; + ss << j; + CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]"); + } + + SECTION("given width") + { + std::stringstream ss; + json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; + ss << std::setw(4) << j; + CHECK(ss.str() == + "[\n \"foo\",\n 1,\n 2,\n 3,\n false,\n {\n \"one\": 1\n }\n]"); + } + } + + SECTION("operator>>") + { + SECTION("no given width") + { + std::stringstream ss; + json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; + j >> ss; + CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]"); + } + + SECTION("given width") + { + std::stringstream ss; + json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; + ss.width(4); + j >> ss; + CHECK(ss.str() == + "[\n \"foo\",\n 1,\n 2,\n 3,\n false,\n {\n \"one\": 1\n }\n]"); + } + } +} diff --git a/test/src/unit-testsuites.cpp b/test/src/unit-testsuites.cpp new file mode 100644 index 00000000..54f09479 --- /dev/null +++ b/test/src/unit-testsuites.cpp @@ -0,0 +1,436 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +#include + +TEST_CASE("compliance tests from json.org") +{ + // test cases are from http://json.org/JSON_checker/ + + SECTION("expected failures") + { + for (auto filename : + { + //"test/data/json_tests/fail1.json", + "test/data/json_tests/fail2.json", + "test/data/json_tests/fail3.json", + "test/data/json_tests/fail4.json", + "test/data/json_tests/fail5.json", + "test/data/json_tests/fail6.json", + "test/data/json_tests/fail7.json", + "test/data/json_tests/fail8.json", + "test/data/json_tests/fail9.json", + "test/data/json_tests/fail10.json", + "test/data/json_tests/fail11.json", + "test/data/json_tests/fail12.json", + "test/data/json_tests/fail13.json", + "test/data/json_tests/fail14.json", + "test/data/json_tests/fail15.json", + "test/data/json_tests/fail16.json", + "test/data/json_tests/fail17.json", + //"test/data/json_tests/fail18.json", + "test/data/json_tests/fail19.json", + "test/data/json_tests/fail20.json", + "test/data/json_tests/fail21.json", + "test/data/json_tests/fail22.json", + "test/data/json_tests/fail23.json", + "test/data/json_tests/fail24.json", + "test/data/json_tests/fail25.json", + "test/data/json_tests/fail26.json", + "test/data/json_tests/fail27.json", + "test/data/json_tests/fail28.json", + "test/data/json_tests/fail29.json", + "test/data/json_tests/fail30.json", + "test/data/json_tests/fail31.json", + "test/data/json_tests/fail32.json", + "test/data/json_tests/fail33.json" + }) + { + CAPTURE(filename); + json j; + std::ifstream f(filename); + CHECK_THROWS_AS(j << f, std::invalid_argument); + } + } + + SECTION("expected passes") + { + for (auto filename : + { + "test/data/json_tests/pass1.json", + "test/data/json_tests/pass2.json", + "test/data/json_tests/pass3.json" + }) + { + CAPTURE(filename); + json j; + std::ifstream f(filename); + CHECK_NOTHROW(j << f); + } + } +} + +TEST_CASE("compliance tests from nativejson-benchmark") +{ + // test cases from https://github.com/miloyip/nativejson-benchmark/blob/master/src/main.cpp + + SECTION("doubles") + { + auto TEST_DOUBLE = [](const std::string & json_string, const double expected) + { + CAPTURE(json_string); + CAPTURE(expected); + CHECK(json::parse(json_string)[0].get() == Approx(expected)); + }; + + TEST_DOUBLE("[0.0]", 0.0); + TEST_DOUBLE("[-0.0]", -0.0); + TEST_DOUBLE("[1.0]", 1.0); + TEST_DOUBLE("[-1.0]", -1.0); + TEST_DOUBLE("[1.5]", 1.5); + TEST_DOUBLE("[-1.5]", -1.5); + TEST_DOUBLE("[3.1416]", 3.1416); + TEST_DOUBLE("[1E10]", 1E10); + TEST_DOUBLE("[1e10]", 1e10); + TEST_DOUBLE("[1E+10]", 1E+10); + TEST_DOUBLE("[1E-10]", 1E-10); + TEST_DOUBLE("[-1E10]", -1E10); + TEST_DOUBLE("[-1e10]", -1e10); + TEST_DOUBLE("[-1E+10]", -1E+10); + TEST_DOUBLE("[-1E-10]", -1E-10); + TEST_DOUBLE("[1.234E+10]", 1.234E+10); + TEST_DOUBLE("[1.234E-10]", 1.234E-10); + TEST_DOUBLE("[1.79769e+308]", 1.79769e+308); + TEST_DOUBLE("[2.22507e-308]", 2.22507e-308); + TEST_DOUBLE("[-1.79769e+308]", -1.79769e+308); + TEST_DOUBLE("[-2.22507e-308]", -2.22507e-308); + TEST_DOUBLE("[4.9406564584124654e-324]", 4.9406564584124654e-324); // minimum denormal + TEST_DOUBLE("[2.2250738585072009e-308]", 2.2250738585072009e-308); // Max subnormal double + TEST_DOUBLE("[2.2250738585072014e-308]", 2.2250738585072014e-308); // Min normal positive double + TEST_DOUBLE("[1.7976931348623157e+308]", 1.7976931348623157e+308); // Max double + TEST_DOUBLE("[1e-10000]", 0.0); // must underflow + TEST_DOUBLE("[18446744073709551616]", + 18446744073709551616.0); // 2^64 (max of uint64_t + 1, force to use double) + TEST_DOUBLE("[-9223372036854775809]", + -9223372036854775809.0); // -2^63 - 1(min of int64_t + 1, force to use double) + TEST_DOUBLE("[0.9868011474609375]", + 0.9868011474609375); // https://github.com/miloyip/rapidjson/issues/120 + TEST_DOUBLE("[123e34]", 123e34); // Fast Path Cases In Disguise + TEST_DOUBLE("[45913141877270640000.0]", 45913141877270640000.0); + TEST_DOUBLE("[2.2250738585072011e-308]", + 2.2250738585072011e-308); + //TEST_DOUBLE("[1e-00011111111111]", 0.0); + //TEST_DOUBLE("[-1e-00011111111111]", -0.0); + TEST_DOUBLE("[1e-214748363]", 0.0); + TEST_DOUBLE("[1e-214748364]", 0.0); + //TEST_DOUBLE("[1e-21474836311]", 0.0); + TEST_DOUBLE("[0.017976931348623157e+310]", 1.7976931348623157e+308); // Max double in another form + + // Since + // abs((2^-1022 - 2^-1074) - 2.2250738585072012e-308) = 3.109754131239141401123495768877590405345064751974375599... ¡Á 10^-324 + // abs((2^-1022) - 2.2250738585072012e-308) = 1.830902327173324040642192159804623318305533274168872044... ¡Á 10 ^ -324 + // So 2.2250738585072012e-308 should round to 2^-1022 = 2.2250738585072014e-308 + TEST_DOUBLE("[2.2250738585072012e-308]", + 2.2250738585072014e-308); + + // More closer to normal/subnormal boundary + // boundary = 2^-1022 - 2^-1075 = 2.225073858507201136057409796709131975934819546351645648... ¡Á 10^-308 + TEST_DOUBLE("[2.22507385850720113605740979670913197593481954635164564e-308]", + 2.2250738585072009e-308); + TEST_DOUBLE("[2.22507385850720113605740979670913197593481954635164565e-308]", + 2.2250738585072014e-308); + + // 1.0 is in (1.0 - 2^-54, 1.0 + 2^-53) + // 1.0 - 2^-54 = 0.999999999999999944488848768742172978818416595458984375 + TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984375]", 1.0); // round to even + TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984374]", + 0.99999999999999989); // previous double + TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984376]", 1.0); // next double + // 1.0 + 2^-53 = 1.00000000000000011102230246251565404236316680908203125 + TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203125]", 1.0); // round to even + TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203124]", 1.0); // previous double + TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203126]", + 1.00000000000000022); // next double + + // Numbers from https://github.com/floitsch/double-conversion/blob/master/test/cctest/test-strtod.cc + + TEST_DOUBLE("[72057594037927928.0]", 72057594037927928.0); + TEST_DOUBLE("[72057594037927936.0]", 72057594037927936.0); + TEST_DOUBLE("[72057594037927932.0]", 72057594037927936.0); + TEST_DOUBLE("[7205759403792793199999e-5]", 72057594037927928.0); + TEST_DOUBLE("[7205759403792793200001e-5]", 72057594037927936.0); + + TEST_DOUBLE("[9223372036854774784.0]", 9223372036854774784.0); + TEST_DOUBLE("[9223372036854775808.0]", 9223372036854775808.0); + TEST_DOUBLE("[9223372036854775296.0]", 9223372036854775808.0); + TEST_DOUBLE("[922337203685477529599999e-5]", 9223372036854774784.0); + TEST_DOUBLE("[922337203685477529600001e-5]", 9223372036854775808.0); + + TEST_DOUBLE("[10141204801825834086073718800384]", 10141204801825834086073718800384.0); + TEST_DOUBLE("[10141204801825835211973625643008]", 10141204801825835211973625643008.0); + TEST_DOUBLE("[10141204801825834649023672221696]", 10141204801825835211973625643008.0); + TEST_DOUBLE("[1014120480182583464902367222169599999e-5]", 10141204801825834086073718800384.0); + TEST_DOUBLE("[1014120480182583464902367222169600001e-5]", 10141204801825835211973625643008.0); + + TEST_DOUBLE("[5708990770823838890407843763683279797179383808]", + 5708990770823838890407843763683279797179383808.0); + TEST_DOUBLE("[5708990770823839524233143877797980545530986496]", + 5708990770823839524233143877797980545530986496.0); + TEST_DOUBLE("[5708990770823839207320493820740630171355185152]", + 5708990770823839524233143877797980545530986496.0); + TEST_DOUBLE("[5708990770823839207320493820740630171355185151999e-3]", + 5708990770823838890407843763683279797179383808.0); + TEST_DOUBLE("[5708990770823839207320493820740630171355185152001e-3]", + 5708990770823839524233143877797980545530986496.0); + + { + char n1e308[312]; // '1' followed by 308 '0' + n1e308[0] = '['; + n1e308[1] = '1'; + for (int j = 2; j < 310; j++) + { + n1e308[j] = '0'; + } + n1e308[310] = ']'; + n1e308[311] = '\0'; + TEST_DOUBLE(n1e308, 1E308); + } + + // Cover trimming + TEST_DOUBLE( + "[2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508" + "7914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012" + "9811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306" + "6665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505" + "1080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621" + "5722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844" + "2390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042" + "7567186443383770486037861622771738545623065874679014086723327636718751234567890123456789012345678901" + "e-308]", + 2.2250738585072014e-308); + } + + SECTION("strings") + { + auto TEST_STRING = [](const std::string & json_string, const std::string & expected) + { + CAPTURE(json_string); + CAPTURE(expected); + CHECK(json::parse(json_string)[0].get() == expected); + }; + + TEST_STRING("[\"\"]", ""); + TEST_STRING("[\"Hello\"]", "Hello"); + TEST_STRING("[\"Hello\\nWorld\"]", "Hello\nWorld"); + //TEST_STRING("[\"Hello\\u0000World\"]", "Hello\0World"); + TEST_STRING("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]", "\"\\/\b\f\n\r\t"); + TEST_STRING("[\"\\u0024\"]", "\x24"); // Dollar sign U+0024 + TEST_STRING("[\"\\u00A2\"]", "\xC2\xA2"); // Cents sign U+00A2 + TEST_STRING("[\"\\u20AC\"]", "\xE2\x82\xAC"); // Euro sign U+20AC + TEST_STRING("[\"\\uD834\\uDD1E\"]", "\xF0\x9D\x84\x9E"); // G clef sign U+1D11E + } + + SECTION("roundtrip") + { + // test cases are from https://github.com/miloyip/nativejson-benchmark/tree/master/test/data/roundtrip + + for (auto filename : + { + "test/data/json_roundtrip/roundtrip01.json", + "test/data/json_roundtrip/roundtrip02.json", + "test/data/json_roundtrip/roundtrip03.json", + "test/data/json_roundtrip/roundtrip04.json", + "test/data/json_roundtrip/roundtrip05.json", + "test/data/json_roundtrip/roundtrip06.json", + "test/data/json_roundtrip/roundtrip07.json", + "test/data/json_roundtrip/roundtrip08.json", + "test/data/json_roundtrip/roundtrip09.json", + "test/data/json_roundtrip/roundtrip10.json", + "test/data/json_roundtrip/roundtrip11.json", + "test/data/json_roundtrip/roundtrip12.json", + "test/data/json_roundtrip/roundtrip13.json", + "test/data/json_roundtrip/roundtrip14.json", + "test/data/json_roundtrip/roundtrip15.json", + "test/data/json_roundtrip/roundtrip16.json", + "test/data/json_roundtrip/roundtrip17.json", + "test/data/json_roundtrip/roundtrip18.json", + "test/data/json_roundtrip/roundtrip19.json", + "test/data/json_roundtrip/roundtrip20.json", + "test/data/json_roundtrip/roundtrip21.json", + "test/data/json_roundtrip/roundtrip22.json", + "test/data/json_roundtrip/roundtrip23.json", + //"test/data/json_roundtrip/roundtrip24.json", // roundtrip error + //"test/data/json_roundtrip/roundtrip25.json", // roundtrip error + //"test/data/json_roundtrip/roundtrip26.json", // roundtrip error + //"test/data/json_roundtrip/roundtrip27.json", // roundtrip error + //"test/data/json_roundtrip/roundtrip28.json", // roundtrip error + "test/data/json_roundtrip/roundtrip29.json", + //"test/data/json_roundtrip/roundtrip30.json", // roundtrip error + //"test/data/json_roundtrip/roundtrip31.json", // roundtrip error + "test/data/json_roundtrip/roundtrip32.json" + }) + { + CAPTURE(filename); + std::ifstream f(filename); + std::string json_string( (std::istreambuf_iterator(f) ), + (std::istreambuf_iterator()) ); + + json j = json::parse(json_string); + CHECK(j.dump() == json_string); + } + } +} + +TEST_CASE("test suite from json-test-suite") +{ + SECTION("read all sample.json") + { + // read a file with all unicode characters stored as single-character + // strings in a JSON array + std::ifstream f("test/data/json_testsuite/sample.json"); + json j; + CHECK_NOTHROW(j << f); + + // the array has 3 elements + CHECK(j.size() == 3); + } +} + +TEST_CASE("json.org examples") +{ + // here, we list all JSON values from http://json.org/example + + SECTION("1.json") + { + std::ifstream f("test/data/json.org/1.json"); + json j; + CHECK_NOTHROW(j << f); + } + + SECTION("2.json") + { + std::ifstream f("test/data/json.org/2.json"); + json j; + CHECK_NOTHROW(j << f); + } + + SECTION("3.json") + { + std::ifstream f("test/data/json.org/3.json"); + json j; + CHECK_NOTHROW(j << f); + } + + SECTION("4.json") + { + std::ifstream f("test/data/json.org/4.json"); + json j; + CHECK_NOTHROW(j << f); + } + + SECTION("5.json") + { + std::ifstream f("test/data/json.org/5.json"); + json j; + CHECK_NOTHROW(j << f); + } +} + +TEST_CASE("RFC 7159 examples") +{ + // here, we list all JSON values from the RFC 7159 document + + SECTION("7. Strings") + { + CHECK(json::parse("\"\\u005C\"") == json("\\")); + CHECK(json::parse("\"\\uD834\\uDD1E\"") == json("𝄞")); + CHECK(json::parse("\"𝄞\"") == json("𝄞")); + } + + SECTION("8.3 String Comparison") + { + CHECK(json::parse("\"a\\b\"") == json::parse("\"a\u005Cb\"")); + } + + SECTION("13 Examples") + { + { + CHECK_NOTHROW(json(R"( + { + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": 100 + }, + "Animated" : false, + "IDs": [116, 943, 234, 38793] + } + } + )")); + } + + { + CHECK_NOTHROW(json(R"( + [ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } + ])")); + } + + CHECK(json::parse("\"Hello world!\"") == json("Hello world!")); + CHECK(json::parse("42") == json(42)); + CHECK(json::parse("true") == json(true)); + } +} diff --git a/test/src/unit-unicode.cpp b/test/src/unit-unicode.cpp new file mode 100644 index 00000000..8efb6154 --- /dev/null +++ b/test/src/unit-unicode.cpp @@ -0,0 +1,176 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +#include + +TEST_CASE("Unicode", "[hide]") +{ + SECTION("full enumeration of Unicode code points") + { + // create an escaped string from a code point + const auto codepoint_to_unicode = [](std::size_t cp) + { + // copd points are represented as a six-character sequence: a + // reverse solidus, followed by the lowercase letter u, followed + // by four hexadecimal digits that encode the character's code + // point + std::stringstream ss; + ss << "\\u" << std::setw(4) << std::setfill('0') << std::hex << cp; + return ss.str(); + }; + + // generate all UTF-8 code points; in total, 1112064 code points are + // generated: 0x1FFFFF code points - 2048 invalid values between + // 0xD800 and 0xDFFF. + for (std::size_t cp = 0; cp <= 0x10FFFFu; ++cp) + { + // The Unicode standard permanently reserves these code point + // values for UTF-16 encoding of the high and low surrogates, and + // they will never be assigned a character, so there should be no + // reason to encode them. The official Unicode standard says that + // no UTF forms, including UTF-16, can encode these code points. + if (cp >= 0xD800u and cp <= 0xDFFFu) + { + // if we would not skip these code points, we would get a + // "missing low surrogate" exception + continue; + } + + // string to store the code point as in \uxxxx format + std::string escaped_string; + // string to store the code point as unescaped character sequence + std::string unescaped_string; + + if (cp < 0x10000u) + { + // code points in the Basic Multilingual Plane can be + // represented with one \\uxxxx sequence + escaped_string = codepoint_to_unicode(cp); + + // All Unicode characters may be placed within the quotation + // marks, except for the characters that must be escaped: + // quotation mark, reverse solidus, and the control characters + // (U+0000 through U+001F); we ignore these code points as + // they are checked with codepoint_to_unicode. + if (cp > 0x1f and cp != 0x22 and cp != 0x5c) + { + unescaped_string = json::lexer::to_unicode(cp); + } + } + else + { + // To escape an extended character that is not in the Basic + // Multilingual Plane, the character is represented as a + // 12-character sequence, encoding the UTF-16 surrogate pair + const auto codepoint1 = 0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu); + const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu); + escaped_string = codepoint_to_unicode(codepoint1); + escaped_string += codepoint_to_unicode(codepoint2); + unescaped_string += json::lexer::to_unicode(codepoint1, codepoint2); + } + + // all other code points are valid and must not yield parse errors + CAPTURE(cp); + CAPTURE(escaped_string); + CAPTURE(unescaped_string); + + json j1, j2, j3, j4; + CHECK_NOTHROW(j1 = json::parse("\"" + escaped_string + "\"")); + CHECK_NOTHROW(j2 = json::parse(j1.dump())); + CHECK(j1 == j2); + + CHECK_NOTHROW(j3 = json::parse("\"" + unescaped_string + "\"")); + CHECK_NOTHROW(j4 = json::parse(j3.dump())); + CHECK(j3 == j4); + } + } + + SECTION("read all unicode characters") + { + // read a file with all unicode characters stored as single-character + // strings in a JSON array + std::ifstream f("test/data/json_nlohmann_tests/all_unicode.json"); + json j; + CHECK_NOTHROW(j << f); + + // the array has 1112064 + 1 elemnts (a terminating "null" value) + // Note: 1112064 = 0x1FFFFF code points - 2048 invalid values between + // 0xD800 and 0xDFFF. + CHECK(j.size() == 1112065); + + SECTION("check JSON Pointers") + { + for (auto s : j) + { + // skip non-string JSON values + if (not s.is_string()) + { + continue; + } + + std::string ptr = s; + + // tilde must be followed by 0 or 1 + if (ptr == "~") + { + ptr += "0"; + } + + // JSON Pointers must begin with "/" + ptr = "/" + ptr; + + CHECK_NOTHROW(json::json_pointer("/" + ptr)); + + // check escape/unescape roundtrip + auto escaped = json::json_pointer::escape(ptr); + json::json_pointer::unescape(escaped); + CHECK(escaped == ptr); + } + } + } + + SECTION("ignore byte-order-mark") + { + // read a file with a UTF-8 BOM + std::ifstream f("test/data/json_nlohmann_tests/bom.json"); + json j; + CHECK_NOTHROW(j << f); + } + + SECTION("error for incomplete/wrong BOM") + { + CHECK_THROWS_AS(json::parse("\xef\xbb"), std::invalid_argument); + CHECK_THROWS_AS(json::parse("\xef\xbb\xbb"), std::invalid_argument); + } +} diff --git a/test/src/unit.cpp b/test/src/unit.cpp index a5c348bb..ec957b7a 100644 --- a/test/src/unit.cpp +++ b/test/src/unit.cpp @@ -26,14382 +26,5 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#define CATCH_CONFIG_MAIN #include "catch.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define private public -#include "json.hpp" -using nlohmann::json; - -// disable float-equal warnings on GCC/clang -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) - #pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - -TEST_CASE("constructors") -{ - SECTION("create an empty value with a given type") - { - SECTION("null") - { - auto t = json::value_t::null; - json j(t); - CHECK(j.type() == t); - } - - SECTION("discarded") - { - auto t = json::value_t::discarded; - json j(t); - CHECK(j.type() == t); - } - - SECTION("object") - { - auto t = json::value_t::object; - json j(t); - CHECK(j.type() == t); - } - - SECTION("array") - { - auto t = json::value_t::array; - json j(t); - CHECK(j.type() == t); - } - - SECTION("boolean") - { - auto t = json::value_t::boolean; - json j(t); - CHECK(j.type() == t); - } - - SECTION("string") - { - auto t = json::value_t::string; - json j(t); - CHECK(j.type() == t); - } - - SECTION("number_integer") - { - auto t = json::value_t::number_integer; - json j(t); - CHECK(j.type() == t); - } - - SECTION("number_unsigned") - { - auto t = json::value_t::number_unsigned; - json j(t); - CHECK(j.type() == t); - } - - SECTION("number_float") - { - auto t = json::value_t::number_float; - json j(t); - CHECK(j.type() == t); - } - } - - SECTION("create a null object (implicitly)") - { - SECTION("no parameter") - { - json j{}; - CHECK(j.type() == json::value_t::null); - } - } - - SECTION("create a null object (explicitly)") - { - SECTION("parameter") - { - json j(nullptr); - CHECK(j.type() == json::value_t::null); - } - } - - SECTION("create an object (explicit)") - { - SECTION("empty object") - { - json::object_t o; - json j(o); - CHECK(j.type() == json::value_t::object); - } - - SECTION("filled object") - { - json::object_t o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; - json j(o); - CHECK(j.type() == json::value_t::object); - } - } - - SECTION("create an object (implicit)") - { - // reference object - json::object_t o_reference {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; - json j_reference(o_reference); - - SECTION("std::map") - { - std::map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; - json j(o); - CHECK(j.type() == json::value_t::object); - CHECK(j == j_reference); - } - - SECTION("std::map") - { - std::map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; - json j(o); - CHECK(j.type() == json::value_t::object); - CHECK(j == j_reference); - } - - SECTION("std::multimap") - { - std::multimap o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; - json j(o); - CHECK(j.type() == json::value_t::object); - CHECK(j == j_reference); - } - - SECTION("std::unordered_map") - { - std::unordered_map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; - json j(o); - CHECK(j.type() == json::value_t::object); - CHECK(j == j_reference); - } - - SECTION("std::unordered_multimap") - { - std::unordered_multimap o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; - json j(o); - CHECK(j.type() == json::value_t::object); - CHECK(j == j_reference); - } - - SECTION("associative container literal") - { - json j({{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}); - CHECK(j.type() == json::value_t::object); - CHECK(j == j_reference); - } - } - - SECTION("create an array (explicit)") - { - SECTION("empty array") - { - json::array_t a; - json j(a); - CHECK(j.type() == json::value_t::array); - } - - SECTION("filled array") - { - json::array_t a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a); - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("create an array (implicit)") - { - // reference array - json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j_reference(a_reference); - - SECTION("std::list") - { - std::list a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a); - CHECK(j.type() == json::value_t::array); - CHECK(j == j_reference); - } - - SECTION("std::forward_list") - { - std::forward_list a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a); - CHECK(j.type() == json::value_t::array); - CHECK(j == j_reference); - } - - SECTION("std::array") - { - std::array a {{json(1), json(1u), json(2.2), json(false), json("string"), json()}}; - json j(a); - CHECK(j.type() == json::value_t::array); - CHECK(j == j_reference); - } - - SECTION("std::vector") - { - std::vector a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a); - CHECK(j.type() == json::value_t::array); - CHECK(j == j_reference); - } - - SECTION("std::deque") - { - std::deque a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a); - CHECK(j.type() == json::value_t::array); - CHECK(j == j_reference); - } - - SECTION("std::set") - { - std::set a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a); - CHECK(j.type() == json::value_t::array); - // we cannot really check for equality here - } - - SECTION("std::unordered_set") - { - std::unordered_set a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a); - CHECK(j.type() == json::value_t::array); - // we cannot really check for equality here - } - - SECTION("sequence container literal") - { - json j({json(1), json(1u), json(2.2), json(false), json("string"), json()}); - CHECK(j.type() == json::value_t::array); - CHECK(j == j_reference); - } - } - - SECTION("create a string (explicit)") - { - SECTION("empty string") - { - json::string_t s; - json j(s); - CHECK(j.type() == json::value_t::string); - } - - SECTION("filled string") - { - json::string_t s {"Hello world"}; - json j(s); - CHECK(j.type() == json::value_t::string); - } - } - - SECTION("create a string (implicit)") - { - // reference string - json::string_t s_reference {"Hello world"}; - json j_reference(s_reference); - - SECTION("std::string") - { - std::string s {"Hello world"}; - json j(s); - CHECK(j.type() == json::value_t::string); - CHECK(j == j_reference); - } - - SECTION("char[]") - { - char s[] {"Hello world"}; - json j(s); - CHECK(j.type() == json::value_t::string); - CHECK(j == j_reference); - } - - SECTION("const char*") - { - const char* s {"Hello world"}; - json j(s); - CHECK(j.type() == json::value_t::string); - CHECK(j == j_reference); - } - - SECTION("string literal") - { - json j("Hello world"); - CHECK(j.type() == json::value_t::string); - CHECK(j == j_reference); - } - } - - SECTION("create a boolean (explicit)") - { - SECTION("empty boolean") - { - json::boolean_t b{}; - json j(b); - CHECK(j.type() == json::value_t::boolean); - } - - SECTION("filled boolean (true)") - { - json j(true); - CHECK(j.type() == json::value_t::boolean); - } - - SECTION("filled boolean (false)") - { - json j(false); - CHECK(j.type() == json::value_t::boolean); - } - } - - SECTION("create an integer number (explicit)") - { - SECTION("uninitialized value") - { - json::number_integer_t n{}; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - } - - SECTION("initialized value") - { - json::number_integer_t n(42); - json j(n); - CHECK(j.type() == json::value_t::number_integer); - } - } - - SECTION("create an integer number (implicit)") - { - // reference objects - json::number_integer_t n_reference = 42; - json j_reference(n_reference); - json::number_unsigned_t n_unsigned_reference = 42; - json j_unsigned_reference(n_unsigned_reference); - - SECTION("short") - { - short n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("unsigned short") - { - unsigned short n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("int") - { - int n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("unsigned int") - { - unsigned int n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("long") - { - long n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("unsigned long") - { - unsigned long n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("long long") - { - long long n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("unsigned long long") - { - unsigned long long n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("int8_t") - { - int8_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int16_t") - { - int16_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int32_t") - { - int32_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int64_t") - { - int64_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_fast8_t") - { - int_fast8_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_fast16_t") - { - int_fast16_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_fast32_t") - { - int_fast32_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_fast64_t") - { - int_fast64_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_least8_t") - { - int_least8_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_least16_t") - { - int_least16_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_least32_t") - { - int_least32_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_least64_t") - { - int_least64_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("uint8_t") - { - uint8_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint16_t") - { - uint16_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint32_t") - { - uint32_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint64_t") - { - uint64_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_fast8_t") - { - uint_fast8_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_fast16_t") - { - uint_fast16_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_fast32_t") - { - uint_fast32_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_fast64_t") - { - uint_fast64_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_least8_t") - { - uint_least8_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_least16_t") - { - uint_least16_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_least32_t") - { - uint_least32_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_least64_t") - { - uint_least64_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("integer literal without suffix") - { - json j(42); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("integer literal with u suffix") - { - json j(42u); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("integer literal with l suffix") - { - json j(42l); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("integer literal with ul suffix") - { - json j(42ul); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("integer literal with ll suffix") - { - json j(42ll); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("integer literal with ull suffix") - { - json j(42ull); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - } - - SECTION("create a floating-point number (explicit)") - { - SECTION("uninitialized value") - { - json::number_float_t n{}; - json j(n); - CHECK(j.type() == json::value_t::number_float); - } - - SECTION("initialized value") - { - json::number_float_t n(42.23); - json j(n); - CHECK(j.type() == json::value_t::number_float); - } - } - - SECTION("create a floating-point number (implicit)") - { - // reference object - json::number_float_t n_reference = 42.23; - json j_reference(n_reference); - - SECTION("float") - { - float n = 42.23; - json j(n); - CHECK(j.type() == json::value_t::number_float); - CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); - } - - SECTION("double") - { - double n = 42.23; - json j(n); - CHECK(j.type() == json::value_t::number_float); - CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); - } - - SECTION("long double") - { - long double n = 42.23; - json j(n); - CHECK(j.type() == json::value_t::number_float); - CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); - } - - SECTION("floating-point literal without suffix") - { - json j(42.23); - CHECK(j.type() == json::value_t::number_float); - CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); - } - - SECTION("integer literal with f suffix") - { - json j(42.23f); - CHECK(j.type() == json::value_t::number_float); - CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); - } - - SECTION("integer literal with l suffix") - { - json j(42.23l); - CHECK(j.type() == json::value_t::number_float); - CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); - } - } - - SECTION("create a container (array or object) from an initializer list") - { - SECTION("empty initializer list") - { - SECTION("explicit") - { - std::initializer_list l; - json j(l); - CHECK(j.type() == json::value_t::object); - } - - SECTION("implicit") - { - json j {}; - CHECK(j.type() == json::value_t::null); - } - } - - SECTION("one element") - { - SECTION("array") - { - SECTION("explicit") - { - std::initializer_list l = {json(json::array_t())}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {json::array_t()}; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("object") - { - SECTION("explicit") - { - std::initializer_list l = {json(json::object_t())}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {json::object_t()}; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("string") - { - SECTION("explicit") - { - std::initializer_list l = {json("Hello world")}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {"Hello world"}; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("boolean") - { - SECTION("explicit") - { - std::initializer_list l = {json(true)}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {true}; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("number (integer)") - { - SECTION("explicit") - { - std::initializer_list l = {json(1)}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {1}; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("number (unsigned)") - { - SECTION("explicit") - { - std::initializer_list l = {json(1u)}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {1u}; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("number (floating-point)") - { - SECTION("explicit") - { - std::initializer_list l = {json(42.23)}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {42.23}; - CHECK(j.type() == json::value_t::array); - } - } - } - - SECTION("more elements") - { - SECTION("explicit") - { - std::initializer_list l = {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()}; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("implicit type deduction") - { - SECTION("object") - { - json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }; - CHECK(j.type() == json::value_t::object); - } - - SECTION("array") - { - json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} , 13 }; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("explicit type deduction") - { - SECTION("empty object") - { - json j = json::object(); - CHECK(j.type() == json::value_t::object); - } - - SECTION("object") - { - json j = json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }); - CHECK(j.type() == json::value_t::object); - } - - SECTION("object with error") - { - CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), - std::logic_error); - CHECK_THROWS_WITH(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), - "cannot create object from initializer list"); - } - - SECTION("empty array") - { - json j = json::array(); - CHECK(j.type() == json::value_t::array); - } - - SECTION("array") - { - json j = json::array({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }); - CHECK(j.type() == json::value_t::array); - } - } - } - - SECTION("create an array of n copies of a given value") - { - json v = {1, "foo", 34.23, {1, 2, 3}, {{"A", 1}, {"B", 2u}}}; - json arr(3, v); - CHECK(arr.size() == 3); - for (auto& x : arr) - { - CHECK(x == v); - } - } - - SECTION("create a JSON container from an iterator range") - { - SECTION("object") - { - SECTION("json(begin(), end())") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json j_new(jobject.begin(), jobject.end()); - CHECK(j_new == jobject); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json j_new(jobject.cbegin(), jobject.cend()); - CHECK(j_new == jobject); - } - } - - SECTION("json(begin(), begin())") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json j_new(jobject.begin(), jobject.begin()); - CHECK(j_new == json::object()); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json j_new(jobject.cbegin(), jobject.cbegin()); - CHECK(j_new == json::object()); - } - } - - SECTION("construct from subrange") - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; - json j_new(jobject.find("b"), jobject.find("e")); - CHECK(j_new == json({{"b", 1}, {"c", 17u}, {"d", false}})); - } - - SECTION("incompatible iterators") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; - json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), std::domain_error); - CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), std::domain_error); - CHECK_THROWS_WITH(json(jobject.begin(), jobject2.end()), "iterators are not compatible"); - CHECK_THROWS_WITH(json(jobject2.begin(), jobject.end()), "iterators are not compatible"); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; - json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), std::domain_error); - CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), std::domain_error); - CHECK_THROWS_WITH(json(jobject.cbegin(), jobject2.cend()), "iterators are not compatible"); - CHECK_THROWS_WITH(json(jobject2.cbegin(), jobject.cend()), "iterators are not compatible"); - } - } - } - - SECTION("array") - { - SECTION("json(begin(), end())") - { - { - json jarray = {1, 2, 3, 4, 5}; - json j_new(jarray.begin(), jarray.end()); - CHECK(j_new == jarray); - } - { - json jarray = {1, 2, 3, 4, 5}; - json j_new(jarray.cbegin(), jarray.cend()); - CHECK(j_new == jarray); - } - } - - SECTION("json(begin(), begin())") - { - { - json jarray = {1, 2, 3, 4, 5}; - json j_new(jarray.begin(), jarray.begin()); - CHECK(j_new == json::array()); - } - { - json jarray = {1, 2, 3, 4, 5}; - json j_new(jarray.cbegin(), jarray.cbegin()); - CHECK(j_new == json::array()); - } - } - - SECTION("construct from subrange") - { - { - json jarray = {1, 2, 3, 4, 5}; - json j_new(jarray.begin() + 1, jarray.begin() + 3); - CHECK(j_new == json({2, 3})); - } - { - json jarray = {1, 2, 3, 4, 5}; - json j_new(jarray.cbegin() + 1, jarray.cbegin() + 3); - CHECK(j_new == json({2, 3})); - } - } - - SECTION("incompatible iterators") - { - { - json jarray = {1, 2, 3, 4}; - json jarray2 = {2, 3, 4, 5}; - CHECK_THROWS_AS(json(jarray.begin(), jarray2.end()), std::domain_error); - CHECK_THROWS_AS(json(jarray2.begin(), jarray.end()), std::domain_error); - CHECK_THROWS_WITH(json(jarray.begin(), jarray2.end()), "iterators are not compatible"); - CHECK_THROWS_WITH(json(jarray2.begin(), jarray.end()), "iterators are not compatible"); - } - { - json jarray = {1, 2, 3, 4}; - json jarray2 = {2, 3, 4, 5}; - CHECK_THROWS_AS(json(jarray.cbegin(), jarray2.cend()), std::domain_error); - CHECK_THROWS_AS(json(jarray2.cbegin(), jarray.cend()), std::domain_error); - CHECK_THROWS_WITH(json(jarray.cbegin(), jarray2.cend()), "iterators are not compatible"); - CHECK_THROWS_WITH(json(jarray2.cbegin(), jarray.cend()), "iterators are not compatible"); - } - } - } - - SECTION("other values") - { - SECTION("construct with two valid iterators") - { - SECTION("null") - { - { - json j; - CHECK_THROWS_AS(json(j.begin(), j.end()), std::domain_error); - CHECK_THROWS_WITH(json(j.begin(), j.end()), "cannot use construct with iterators from null"); - } - { - json j; - CHECK_THROWS_AS(json(j.cbegin(), j.cend()), std::domain_error); - CHECK_THROWS_WITH(json(j.cbegin(), j.cend()), "cannot use construct with iterators from null"); - } - } - - SECTION("string") - { - { - json j = "foo"; - json j_new(j.begin(), j.end()); - CHECK(j == j_new); - } - { - json j = "bar"; - json j_new(j.cbegin(), j.cend()); - CHECK(j == j_new); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - json j_new(j.begin(), j.end()); - CHECK(j == j_new); - } - { - json j = true; - json j_new(j.cbegin(), j.cend()); - CHECK(j == j_new); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - json j_new(j.begin(), j.end()); - CHECK(j == j_new); - } - { - json j = 17; - json j_new(j.cbegin(), j.cend()); - CHECK(j == j_new); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - json j_new(j.begin(), j.end()); - CHECK(j == j_new); - } - { - json j = 17u; - json j_new(j.cbegin(), j.cend()); - CHECK(j == j_new); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - json j_new(j.begin(), j.end()); - CHECK(j == j_new); - } - { - json j = 23.42; - json j_new(j.cbegin(), j.cend()); - CHECK(j == j_new); - } - } - } - - SECTION("construct with two invalid iterators") - { - SECTION("string") - { - { - json j = "foo"; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = "bar"; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = true; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 17; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (integer)") - { - { - json j = 17u; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 17u; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 23.42; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - } - } - } - - SECTION("create a JSON value from an input stream") - { - SECTION("std::stringstream") - { - std::stringstream ss; - ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; - json j(ss); - CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); - } - - SECTION("with callback function") - { - std::stringstream ss; - ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; - json j(ss, [](int, json::parse_event_t, const json & val) - { - // filter all number(2) elements - if (val == json(2)) - { - return false; - } - else - { - return true; - } - }); - CHECK(j == json({"foo", 1, 3, false, {{"one", 1}}})); - } - - SECTION("std::ifstream") - { - std::ifstream f("test/data/json_tests/pass1.json"); - json j(f); - } - } -} - -TEST_CASE("other constructors and destructor") -{ - SECTION("copy constructor") - { - SECTION("object") - { - json j {{"foo", 1}, {"bar", false}}; - json k(j); - CHECK(j == k); - } - - SECTION("array") - { - json j {"foo", 1, 42.23, false}; - json k(j); - CHECK(j == k); - } - - SECTION("null") - { - json j(nullptr); - json k(j); - CHECK(j == k); - } - - SECTION("boolean") - { - json j(true); - json k(j); - CHECK(j == k); - } - - SECTION("string") - { - json j("Hello world"); - json k(j); - CHECK(j == k); - } - - SECTION("number (integer)") - { - json j(42); - json k(j); - CHECK(j == k); - } - - SECTION("number (unsigned)") - { - json j(42u); - json k(j); - CHECK(j == k); - } - - SECTION("number (floating-point)") - { - json j(42.23); - json k(j); - CHECK(j == k); - } - } - - SECTION("move constructor") - { - json j {{"foo", "bar"}, {"baz", {1, 2, 3, 4}}, {"a", 42u}, {"b", 42.23}, {"c", nullptr}}; - CHECK(j.type() == json::value_t::object); - json k(std::move(j)); - CHECK(k.type() == json::value_t::object); - CHECK(j.type() == json::value_t::null); - } - - SECTION("copy assignment") - { - SECTION("object") - { - json j {{"foo", 1}, {"bar", false}}; - json k; - k = j; - CHECK(j == k); - } - - SECTION("array") - { - json j {"foo", 1, 42.23, false}; - json k; - k = j; - CHECK(j == k); - } - - SECTION("null") - { - json j(nullptr); - json k; - k = j; - CHECK(j == k); - } - - SECTION("boolean") - { - json j(true); - json k; - k = j; - CHECK(j == k); - } - - SECTION("string") - { - json j("Hello world"); - json k; - k = j; - CHECK(j == k); - } - - SECTION("number (integer)") - { - json j(42); - json k; - k = j; - CHECK(j == k); - } - - SECTION("number (unsigned)") - { - json j(42u); - json k; - k = j; - CHECK(j == k); - } - - SECTION("number (floating-point)") - { - json j(42.23); - json k; - k = j; - CHECK(j == k); - } - } - - SECTION("destructor") - { - SECTION("object") - { - auto j = new json {{"foo", 1}, {"bar", false}}; - delete j; - } - - SECTION("array") - { - auto j = new json {"foo", 1, 1u, false, 23.42}; - delete j; - } - - SECTION("string") - { - auto j = new json("Hello world"); - delete j; - } - } -} - -TEST_CASE("object inspection") -{ - SECTION("convenience type checker") - { - SECTION("object") - { - json j {{"foo", 1}, {"bar", false}}; - CHECK(not j.is_null()); - CHECK(not j.is_boolean()); - CHECK(not j.is_number()); - CHECK(not j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(j.is_object()); - CHECK(not j.is_array()); - CHECK(not j.is_string()); - CHECK(not j.is_discarded()); - CHECK(not j.is_primitive()); - CHECK(j.is_structured()); - } - - SECTION("array") - { - json j {"foo", 1, 1u, 42.23, false}; - CHECK(not j.is_null()); - CHECK(not j.is_boolean()); - CHECK(not j.is_number()); - CHECK(not j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(not j.is_object()); - CHECK(j.is_array()); - CHECK(not j.is_string()); - CHECK(not j.is_discarded()); - CHECK(not j.is_primitive()); - CHECK(j.is_structured()); - } - - SECTION("null") - { - json j(nullptr); - CHECK(j.is_null()); - CHECK(not j.is_boolean()); - CHECK(not j.is_number()); - CHECK(not j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(not j.is_object()); - CHECK(not j.is_array()); - CHECK(not j.is_string()); - CHECK(not j.is_discarded()); - CHECK(j.is_primitive()); - CHECK(not j.is_structured()); - } - - SECTION("boolean") - { - json j(true); - CHECK(not j.is_null()); - CHECK(j.is_boolean()); - CHECK(not j.is_number()); - CHECK(not j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(not j.is_object()); - CHECK(not j.is_array()); - CHECK(not j.is_string()); - CHECK(not j.is_discarded()); - CHECK(j.is_primitive()); - CHECK(not j.is_structured()); - } - - SECTION("string") - { - json j("Hello world"); - CHECK(not j.is_null()); - CHECK(not j.is_boolean()); - CHECK(not j.is_number()); - CHECK(not j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(not j.is_object()); - CHECK(not j.is_array()); - CHECK(j.is_string()); - CHECK(not j.is_discarded()); - CHECK(j.is_primitive()); - CHECK(not j.is_structured()); - } - - SECTION("number (integer)") - { - json j(42); - CHECK(not j.is_null()); - CHECK(not j.is_boolean()); - CHECK(j.is_number()); - CHECK(j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(not j.is_object()); - CHECK(not j.is_array()); - CHECK(not j.is_string()); - CHECK(not j.is_discarded()); - CHECK(j.is_primitive()); - CHECK(not j.is_structured()); - } - - SECTION("number (unsigned)") - { - json j(42u); - CHECK(not j.is_null()); - CHECK(not j.is_boolean()); - CHECK(j.is_number()); - CHECK(j.is_number_integer()); - CHECK(j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(not j.is_object()); - CHECK(not j.is_array()); - CHECK(not j.is_string()); - CHECK(not j.is_discarded()); - CHECK(j.is_primitive()); - CHECK(not j.is_structured()); - } - - SECTION("number (floating-point)") - { - json j(42.23); - CHECK(not j.is_null()); - CHECK(not j.is_boolean()); - CHECK(j.is_number()); - CHECK(not j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(j.is_number_float()); - CHECK(not j.is_object()); - CHECK(not j.is_array()); - CHECK(not j.is_string()); - CHECK(not j.is_discarded()); - CHECK(j.is_primitive()); - CHECK(not j.is_structured()); - } - - SECTION("discarded") - { - json j(json::value_t::discarded); - CHECK(not j.is_null()); - CHECK(not j.is_boolean()); - CHECK(not j.is_number()); - CHECK(not j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(not j.is_object()); - CHECK(not j.is_array()); - CHECK(not j.is_string()); - CHECK(j.is_discarded()); - CHECK(not j.is_primitive()); - CHECK(not j.is_structured()); - } - } - - SECTION("serialization") - { - json j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; - - SECTION("no indent / indent=-1") - { - CHECK(j.dump() == - "{\"array\":[1,2,3,4],\"boolean\":false,\"null\":null,\"number\":42,\"object\":{},\"string\":\"Hello world\"}"); - - CHECK(j.dump() == j.dump(-1)); - } - - SECTION("indent=0") - { - CHECK(j.dump(0) == - "{\n\"array\": [\n1,\n2,\n3,\n4\n],\n\"boolean\": false,\n\"null\": null,\n\"number\": 42,\n\"object\": {},\n\"string\": \"Hello world\"\n}"); - } - - SECTION("indent=4") - { - CHECK(j.dump(4) == - "{\n \"array\": [\n 1,\n 2,\n 3,\n 4\n ],\n \"boolean\": false,\n \"null\": null,\n \"number\": 42,\n \"object\": {},\n \"string\": \"Hello world\"\n}"); - } - - SECTION("dump and floating-point numbers") - { - auto s = json(42.23).dump(); - CHECK(s.find("42.23") != std::string::npos); - } - - SECTION("dump and small floating-point numbers") - { - auto s = json(1.23456e-78).dump(); - CHECK(s.find("1.23456e-78") != std::string::npos); - } - - SECTION("dump and non-ASCII characters") - { - CHECK(json("ä").dump() == "\"ä\""); - CHECK(json("Ö").dump() == "\"Ö\""); - CHECK(json("❤️").dump() == "\"❤️\""); - } - - SECTION("serialization of discarded element") - { - json j_discarded(json::value_t::discarded); - CHECK(j_discarded.dump() == ""); - } - - SECTION("check that precision is reset after serialization") - { - // create stringstream and set precision - std::stringstream ss; - ss.precision(3); - ss << 3.141592653589793 << std::fixed; - CHECK(ss.str() == "3.14"); - - // reset stringstream - ss.str(std::string()); - - // use stringstream for JSON serialization - json j_number = 3.141592653589793; - ss << j_number; - - // check that precision has been overridden during serialization - CHECK(ss.str() == "3.141592653589793"); - - // check that precision has been restored - CHECK(ss.precision() == 3); - } - } - - SECTION("return the type of the object (explicit)") - { - SECTION("null") - { - json j = nullptr; - CHECK(j.type() == json::value_t::null); - } - - SECTION("object") - { - json j = {{"foo", "bar"}}; - CHECK(j.type() == json::value_t::object); - } - - SECTION("array") - { - json j = {1, 2, 3, 4}; - CHECK(j.type() == json::value_t::array); - } - - SECTION("boolean") - { - json j = true; - CHECK(j.type() == json::value_t::boolean); - } - - SECTION("string") - { - json j = "Hello world"; - CHECK(j.type() == json::value_t::string); - } - - SECTION("number (integer)") - { - json j = 23; - CHECK(j.type() == json::value_t::number_integer); - } - - SECTION("number (unsigned)") - { - json j = 23u; - CHECK(j.type() == json::value_t::number_unsigned); - } - - SECTION("number (floating-point)") - { - json j = 42.23; - CHECK(j.type() == json::value_t::number_float); - } - } - - SECTION("return the type of the object (implicit)") - { - SECTION("null") - { - json j = nullptr; - json::value_t t = j; - CHECK(t == j.type()); - } - - SECTION("object") - { - json j = {{"foo", "bar"}}; - json::value_t t = j; - CHECK(t == j.type()); - } - - SECTION("array") - { - json j = {1, 2, 3, 4}; - json::value_t t = j; - CHECK(t == j.type()); - } - - SECTION("boolean") - { - json j = true; - json::value_t t = j; - CHECK(t == j.type()); - } - - SECTION("string") - { - json j = "Hello world"; - json::value_t t = j; - CHECK(t == j.type()); - } - - SECTION("number (integer)") - { - json j = 23; - json::value_t t = j; - CHECK(t == j.type()); - } - - SECTION("number (unsigned)") - { - json j = 23u; - json::value_t t = j; - CHECK(t == j.type()); - } - - SECTION("number (floating-point)") - { - json j = 42.23; - json::value_t t = j; - CHECK(t == j.type()); - } - } -} - -TEST_CASE("value conversion") -{ - SECTION("get an object (explicit)") - { - json::object_t o_reference = {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; - json j(o_reference); - - SECTION("json::object_t") - { - json::object_t o = j.get(); - CHECK(json(o) == j); - } - - SECTION("std::map") - { - std::map o = j.get>(); - CHECK(json(o) == j); - } - - SECTION("std::multimap") - { - std::multimap o = j.get>(); - CHECK(json(o) == j); - } - - SECTION("std::unordered_map") - { - std::unordered_map o = j.get>(); - CHECK(json(o) == j); - } - - SECTION("std::unordered_multimap") - { - std::unordered_multimap o = - j.get>(); - CHECK(json(o) == j); - } - - SECTION("exception in case of a non-object type") - { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); - - CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be object, but is null"); - CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be object, but is array"); - CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be object, but is string"); - CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be object, but is boolean"); - CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), - "type must be object, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), - "type must be object, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_float).get(), - "type must be object, but is number"); - } - } - - SECTION("get an object (implicit)") - { - json::object_t o_reference = {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; - json j(o_reference); - - SECTION("json::object_t") - { - json::object_t o = j; - CHECK(json(o) == j); - } - - SECTION("std::map") - { - std::map o = j; - CHECK(json(o) == j); - } - - SECTION("std::multimap") - { - std::multimap o = j; - CHECK(json(o) == j); - } - - SECTION("std::unordered_map") - { - std::unordered_map o = j; - CHECK(json(o) == j); - } - - SECTION("std::unordered_multimap") - { - std::unordered_multimap o = j; - CHECK(json(o) == j); - } - } - - SECTION("get an array (explicit)") - { - json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a_reference); - - SECTION("json::array_t") - { - json::array_t a = j.get(); - CHECK(json(a) == j); - } - - SECTION("std::list") - { - std::list a = j.get>(); - CHECK(json(a) == j); - } - - SECTION("std::forward_list") - { - std::forward_list a = j.get>(); - CHECK(json(a) == j); - } - - SECTION("std::vector") - { - std::vector a = j.get>(); - CHECK(json(a) == j); - } - - SECTION("std::deque") - { - std::deque a = j.get>(); - CHECK(json(a) == j); - } - - SECTION("exception in case of a non-array type") - { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); - - CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be array, but is null"); - CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be array, but is object"); - CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be array, but is string"); - CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be array, but is boolean"); - CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), - "type must be array, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), - "type must be array, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_float).get(), - "type must be array, but is number"); - } - } - - SECTION("get an array (implicit)") - { - json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a_reference); - - SECTION("json::array_t") - { - json::array_t a = j; - CHECK(json(a) == j); - } - - SECTION("std::list") - { - std::list a = j; - CHECK(json(a) == j); - } - - SECTION("std::forward_list") - { - std::forward_list a = j; - CHECK(json(a) == j); - } - - SECTION("std::vector") - { - std::vector a = j; - CHECK(json(a) == j); - } - - SECTION("std::deque") - { - std::deque a = j; - CHECK(json(a) == j); - } - } - - SECTION("get a string (explicit)") - { - json::string_t s_reference {"Hello world"}; - json j(s_reference); - - SECTION("string_t") - { - json::string_t s = j.get(); - CHECK(json(s) == j); - } - - SECTION("std::string") - { - std::string s = j.get(); - CHECK(json(s) == j); - } - - SECTION("exception in case of a non-string type") - { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); - - CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be string, but is null"); - CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be string, but is object"); - CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be string, but is array"); - CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be string, but is boolean"); - CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), - "type must be string, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), - "type must be string, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_float).get(), - "type must be string, but is number"); - } - } - - SECTION("get a string (implicit)") - { - json::string_t s_reference {"Hello world"}; - json j(s_reference); - - SECTION("string_t") - { - json::string_t s = j; - CHECK(json(s) == j); - } - - SECTION("std::string") - { - std::string s = j; - CHECK(json(s) == j); - } - } - - SECTION("get a boolean (explicit)") - { - json::boolean_t b_reference {true}; - json j(b_reference); - - SECTION("boolean_t") - { - json::boolean_t b = j.get(); - CHECK(json(b) == j); - } - - SECTION("bool") - { - bool b = j.get(); - CHECK(json(b) == j); - } - - SECTION("exception in case of a non-string type") - { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); - - CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be boolean, but is null"); - CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be boolean, but is object"); - CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be boolean, but is array"); - CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be boolean, but is string"); - CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), - "type must be boolean, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), - "type must be boolean, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_float).get(), - "type must be boolean, but is number"); - } - } - - SECTION("get a boolean (implicit)") - { - json::boolean_t b_reference {true}; - json j(b_reference); - - SECTION("boolean_t") - { - json::boolean_t b = j; - CHECK(json(b) == j); - } - - SECTION("bool") - { - bool b = j; - CHECK(json(b) == j); - } - } - - SECTION("get an integer number (explicit)") - { - json::number_integer_t n_reference {42}; - json j(n_reference); - json::number_unsigned_t n_unsigned_reference {42u}; - json j_unsigned(n_unsigned_reference); - - SECTION("number_integer_t") - { - json::number_integer_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("number_unsigned_t") - { - json::number_unsigned_t n = j_unsigned.get(); - CHECK(json(n) == j_unsigned); - } - - SECTION("short") - { - short n = j.get(); - CHECK(json(n) == j); - } - - SECTION("unsigned short") - { - unsigned short n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int") - { - int n = j.get(); - CHECK(json(n) == j); - } - - SECTION("unsigned int") - { - unsigned int n = j.get(); - CHECK(json(n) == j); - } - - SECTION("long") - { - long n = j.get(); - CHECK(json(n) == j); - } - - SECTION("unsigned long") - { - unsigned long n = j.get(); - CHECK(json(n) == j); - } - - SECTION("long long") - { - long long n = j.get(); - CHECK(json(n) == j); - } - - SECTION("unsigned long long") - { - unsigned long long n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int8_t") - { - int8_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int16_t") - { - int16_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int32_t") - { - int32_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int64_t") - { - int64_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int8_fast_t") - { - int_fast8_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int16_fast_t") - { - int_fast16_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int32_fast_t") - { - int_fast32_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int64_fast_t") - { - int_fast64_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int8_least_t") - { - int_least8_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int16_least_t") - { - int_least16_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int32_least_t") - { - int_least32_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int64_least_t") - { - int_least64_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint8_t") - { - uint8_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint16_t") - { - uint16_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint32_t") - { - uint32_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint64_t") - { - uint64_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint8_fast_t") - { - uint_fast8_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint16_fast_t") - { - uint_fast16_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint32_fast_t") - { - uint_fast32_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint64_fast_t") - { - uint_fast64_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint8_least_t") - { - uint_least8_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint16_least_t") - { - uint_least16_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint32_least_t") - { - uint_least32_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint64_least_t") - { - uint_least64_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("exception in case of a non-number type") - { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); - - CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be number, but is null"); - CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be number, but is object"); - CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be number, but is array"); - CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be number, but is string"); - CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be number, but is boolean"); - - CHECK_NOTHROW(json(json::value_t::number_float).get()); - CHECK_NOTHROW(json(json::value_t::number_float).get()); - } - } - - SECTION("get an integer number (implicit)") - { - json::number_integer_t n_reference {42}; - json j(n_reference); - json::number_unsigned_t n_unsigned_reference {42u}; - json j_unsigned(n_unsigned_reference); - - SECTION("number_integer_t") - { - json::number_integer_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("number_unsigned_t") - { - json::number_unsigned_t n = j_unsigned.get(); - CHECK(json(n) == j_unsigned); - } - - SECTION("short") - { - short n = j; - CHECK(json(n) == j); - } - - SECTION("unsigned short") - { - unsigned short n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("int") - { - int n = j; - CHECK(json(n) == j); - } - - SECTION("unsigned int") - { - unsigned int n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("long") - { - long n = j; - CHECK(json(n) == j); - } - - SECTION("unsigned long") - { - unsigned long n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("long long") - { - long long n = j; - CHECK(json(n) == j); - } - - SECTION("unsigned long long") - { - unsigned long long n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("int8_t") - { - int8_t n = j; - CHECK(json(n) == j); - } - - SECTION("int16_t") - { - int16_t n = j; - CHECK(json(n) == j); - } - - SECTION("int32_t") - { - int32_t n = j; - CHECK(json(n) == j); - } - - SECTION("int64_t") - { - int64_t n = j; - CHECK(json(n) == j); - } - - SECTION("int8_fast_t") - { - int_fast8_t n = j; - CHECK(json(n) == j); - } - - SECTION("int16_fast_t") - { - int_fast16_t n = j; - CHECK(json(n) == j); - } - - SECTION("int32_fast_t") - { - int_fast32_t n = j; - CHECK(json(n) == j); - } - - SECTION("int64_fast_t") - { - int_fast64_t n = j; - CHECK(json(n) == j); - } - - SECTION("int8_least_t") - { - int_least8_t n = j; - CHECK(json(n) == j); - } - - SECTION("int16_least_t") - { - int_least16_t n = j; - CHECK(json(n) == j); - } - - SECTION("int32_least_t") - { - int_least32_t n = j; - CHECK(json(n) == j); - } - - SECTION("int64_least_t") - { - int_least64_t n = j; - CHECK(json(n) == j); - } - - SECTION("uint8_t") - { - uint8_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint16_t") - { - uint16_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint32_t") - { - uint32_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint64_t") - { - uint64_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint8_fast_t") - { - uint_fast8_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint16_fast_t") - { - uint_fast16_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint32_fast_t") - { - uint_fast32_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint64_fast_t") - { - uint_fast64_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint8_least_t") - { - uint_least8_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint16_least_t") - { - uint_least16_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint32_least_t") - { - uint_least32_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint64_least_t") - { - uint_least64_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - } - - SECTION("get a floating-point number (explicit)") - { - json::number_float_t n_reference {42.23}; - json j(n_reference); - - SECTION("number_float_t") - { - json::number_float_t n = j.get(); - CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); - } - - SECTION("float") - { - float n = j.get(); - CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); - } - - SECTION("double") - { - double n = j.get(); - CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); - } - - SECTION("exception in case of a non-string type") - { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); - - CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be number, but is null"); - CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be number, but is object"); - CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be number, but is array"); - CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be number, but is string"); - CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be number, but is boolean"); - - CHECK_NOTHROW(json(json::value_t::number_integer).get()); - CHECK_NOTHROW(json(json::value_t::number_unsigned).get()); - } - } - - SECTION("get a floating-point number (implicit)") - { - json::number_float_t n_reference {42.23}; - json j(n_reference); - - SECTION("number_float_t") - { - json::number_float_t n = j; - CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); - } - - SECTION("float") - { - float n = j; - CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); - } - - SECTION("double") - { - double n = j; - CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); - } - } - - SECTION("more involved conversions") - { - SECTION("object-like STL containers") - { - json j1 = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j2 = {{"one", 1u}, {"two", 2u}, {"three", 3u}}; - json j3 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}; - json j4 = {{"one", true}, {"two", false}, {"three", true}}; - json j5 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}; - - SECTION("std::map") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - //auto m5 = j5.get>(); - } - - SECTION("std::unordered_map") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - //auto m5 = j5.get>(); - //CHECK(m5["one"] == "eins"); - } - - SECTION("std::multimap") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - //auto m5 = j5.get>(); - //CHECK(m5["one"] == "eins"); - } - - SECTION("std::unordered_multimap") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - //auto m5 = j5.get>(); - //CHECK(m5["one"] == "eins"); - } - - SECTION("exception in case of a non-object type") - { - CHECK_THROWS_AS((json().get>()), std::logic_error); - CHECK_THROWS_WITH((json().get>()), "type must be object, but is null"); - } - } - - SECTION("array-like STL containers") - { - json j1 = {1, 2, 3, 4}; - json j2 = {1u, 2u, 3u, 4u}; - json j3 = {1.2, 2.3, 3.4, 4.5}; - json j4 = {true, false, true}; - json j5 = {"one", "two", "three"}; - - SECTION("std::list") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - auto m5 = j5.get>(); - } - - //SECTION("std::forward_list") - //{ - // auto m1 = j1.get>(); - // auto m2 = j2.get>(); - // auto m3 = j3.get>(); - // auto m4 = j4.get>(); - // auto m5 = j5.get>(); - //} - - SECTION("std::vector") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - auto m5 = j5.get>(); - } - - SECTION("std::deque") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j2.get>(); - auto m4 = j4.get>(); - auto m5 = j5.get>(); - } - - SECTION("std::set") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - auto m5 = j5.get>(); - } - - SECTION("std::unordered_set") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - auto m5 = j5.get>(); - } - - SECTION("exception in case of a non-object type") - { - CHECK_THROWS_AS((json().get>()), std::logic_error); - CHECK_THROWS_AS((json().get>()), std::logic_error); - CHECK_THROWS_AS((json().get>()), std::logic_error); - CHECK_THROWS_AS((json().get>()), std::logic_error); - - CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); - CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); - CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); - CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); - } - } - } -} - -TEST_CASE("pointer access") -{ - // create a JSON value with different types - json json_types = - { - {"boolean", true}, - { - "number", { - {"integer", 42}, - {"unsigned", 42u}, - {"floating-point", 17.23} - } - }, - {"string", "Hello, world!"}, - {"array", {1, 2, 3, 4, 5}}, - {"null", nullptr} - }; - - SECTION("pointer access to object_t") - { - using test_type = json::object_t; - json value = {{"one", 1}, {"two", 2}}; - - // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p1 == value.get()); - - const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p2 == value.get()); - - const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p3 == value.get()); - - // check if null pointers are returned correctly - CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - } - - SECTION("pointer access to const object_t") - { - using test_type = json::object_t; - const json value = {{"one", 1}, {"two", 2}}; - - // this should not compile - // test_type* p1 = value.get_ptr(); - - // check if pointers are returned correctly - const test_type* p2 = value.get_ptr(); - CHECK(*p2 == value.get()); - - const test_type* const p3 = value.get_ptr(); - CHECK(p2 == p3); - } - - SECTION("pointer access to array_t") - { - using test_type = json::array_t; - json value = {1, 2, 3, 4}; - - // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p1 == value.get()); - - const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p2 == value.get()); - - const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p3 == value.get()); - - // check if null pointers are returned correctly - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - } - - SECTION("pointer access to string_t") - { - using test_type = json::string_t; - json value = "hello"; - - // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p1 == value.get()); - - const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p2 == value.get()); - - const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p3 == value.get()); - - // check if null pointers are returned correctly - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - } - - SECTION("pointer access to boolean_t") - { - using test_type = json::boolean_t; - json value = false; - - // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p1 == value.get()); - - const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p2 == value.get()); - - const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p3 == value.get()); - - // check if null pointers are returned correctly - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - } - - SECTION("pointer access to number_integer_t") - { - using test_type = json::number_integer_t; - json value = 23; - - // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p1 == value.get()); - - const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p2 == value.get()); - - const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p3 == value.get()); - - // check if null pointers are returned correctly - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - } - - SECTION("pointer access to number_unsigned_t") - { - using test_type = json::number_unsigned_t; - json value = 23u; - - // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p1 == value.get()); - - const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p2 == value.get()); - - const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p3 == value.get()); - - // check if null pointers are returned correctly - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); - } - - SECTION("pointer access to number_float_t") - { - using test_type = json::number_float_t; - json value = 42.23; - - // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p1 == Approx(value.get())); - - const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p2 == Approx(value.get())); - - const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p3 == Approx(value.get())); - - // check if null pointers are returned correctly - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); - } -} - -TEST_CASE("reference access") -{ - // create a JSON value with different types - json json_types = - { - {"boolean", true}, - { - "number", { - {"integer", 42}, - {"floating-point", 17.23} - } - }, - {"string", "Hello, world!"}, - {"array", {1, 2, 3, 4, 5}}, - {"null", nullptr} - }; - - SECTION("reference access to object_t") - { - using test_type = json::object_t; - json value = {{"one", 1}, {"two", 2}}; - - // check if references are returned correctly - test_type& p1 = value.get_ref(); - CHECK(&p1 == value.get_ptr()); - CHECK(p1 == value.get()); - - const test_type& p2 = value.get_ref(); - CHECK(&p2 == value.get_ptr()); - CHECK(p2 == value.get()); - - // check if mismatching references throw correctly - CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - } - - SECTION("const reference access to const object_t") - { - using test_type = json::object_t; - const json value = {{"one", 1}, {"two", 2}}; - - // this should not compile - // test_type& p1 = value.get_ref(); - - // check if references are returned correctly - const test_type& p2 = value.get_ref(); - CHECK(&p2 == value.get_ptr()); - CHECK(p2 == value.get()); - } - - SECTION("reference access to array_t") - { - using test_type = json::array_t; - json value = {1, 2, 3, 4}; - - // check if references are returned correctly - test_type& p1 = value.get_ref(); - CHECK(&p1 == value.get_ptr()); - CHECK(p1 == value.get()); - - const test_type& p2 = value.get_ref(); - CHECK(&p2 == value.get_ptr()); - CHECK(p2 == value.get()); - - // check if mismatching references throw correctly - CHECK_THROWS(value.get_ref()); - CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - } - - SECTION("reference access to string_t") - { - using test_type = json::string_t; - json value = "hello"; - - // check if references are returned correctly - test_type& p1 = value.get_ref(); - CHECK(&p1 == value.get_ptr()); - CHECK(p1 == value.get()); - - const test_type& p2 = value.get_ref(); - CHECK(&p2 == value.get_ptr()); - CHECK(p2 == value.get()); - - // check if mismatching references throw correctly - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - } - - SECTION("reference access to boolean_t") - { - using test_type = json::boolean_t; - json value = false; - - // check if references are returned correctly - test_type& p1 = value.get_ref(); - CHECK(&p1 == value.get_ptr()); - CHECK(p1 == value.get()); - - const test_type& p2 = value.get_ref(); - CHECK(&p2 == value.get_ptr()); - CHECK(p2 == value.get()); - - // check if mismatching references throw correctly - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - } - - SECTION("reference access to number_integer_t") - { - using test_type = json::number_integer_t; - json value = 23; - - // check if references are returned correctly - test_type& p1 = value.get_ref(); - CHECK(&p1 == value.get_ptr()); - CHECK(p1 == value.get()); - - const test_type& p2 = value.get_ref(); - CHECK(&p2 == value.get_ptr()); - CHECK(p2 == value.get()); - - // check if mismatching references throw correctly - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS(value.get_ref()); - } - - SECTION("reference access to number_float_t") - { - using test_type = json::number_float_t; - json value = 42.23; - - // check if references are returned correctly - test_type& p1 = value.get_ref(); - CHECK(&p1 == value.get_ptr()); - CHECK(p1 == value.get()); - - const test_type& p2 = value.get_ref(); - CHECK(&p2 == value.get_ptr()); - CHECK(p2 == value.get()); - - // check if mismatching references throw correctly - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_NOTHROW(value.get_ref()); - } -} - -TEST_CASE("element access") -{ - SECTION("array") - { - json j = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - const json j_const = j; - - SECTION("access specified element with bounds checking") - { - SECTION("access within bounds") - { - CHECK(j.at(0) == json(1)); - CHECK(j.at(1) == json(1u)); - CHECK(j.at(2) == json(true)); - CHECK(j.at(3) == json(nullptr)); - CHECK(j.at(4) == json("string")); - CHECK(j.at(5) == json(42.23)); - CHECK(j.at(6) == json(json::object())); - CHECK(j.at(7) == json({1, 2, 3})); - - CHECK(j_const.at(0) == json(1)); - CHECK(j_const.at(1) == json(1u)); - CHECK(j_const.at(2) == json(true)); - CHECK(j_const.at(3) == json(nullptr)); - CHECK(j_const.at(4) == json("string")); - CHECK(j_const.at(5) == json(42.23)); - CHECK(j_const.at(6) == json(json::object())); - CHECK(j_const.at(7) == json({1, 2, 3})); - } - - SECTION("access outside bounds") - { - CHECK_THROWS_AS(j.at(8), std::out_of_range); - CHECK_THROWS_AS(j_const.at(8), std::out_of_range); - - CHECK_THROWS_WITH(j.at(8), "array index 8 is out of range"); - CHECK_THROWS_WITH(j_const.at(8), "array index 8 is out of range"); - } - - SECTION("access on non-array type") - { - SECTION("null") - { - json j_nonarray(json::value_t::null); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with null"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with null"); - } - - SECTION("boolean") - { - json j_nonarray(json::value_t::boolean); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with boolean"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with boolean"); - } - - SECTION("string") - { - json j_nonarray(json::value_t::string); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with string"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with string"); - } - - SECTION("object") - { - json j_nonarray(json::value_t::object); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with object"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with object"); - } - - SECTION("number (integer)") - { - json j_nonarray(json::value_t::number_integer); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); - } - - SECTION("number (unsigned)") - { - json j_nonarray(json::value_t::number_unsigned); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); - } - - SECTION("number (floating-point)") - { - json j_nonarray(json::value_t::number_float); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); - } - } - } - - SECTION("front and back") - { - CHECK(j.front() == json(1)); - CHECK(j_const.front() == json(1)); - CHECK(j.back() == json({1, 2, 3})); - CHECK(j_const.back() == json({1, 2, 3})); - } - - SECTION("access specified element") - { - SECTION("access within bounds") - { - CHECK(j[0] == json(1)); - CHECK(j[1] == json(1u)); - CHECK(j[2] == json(true)); - CHECK(j[3] == json(nullptr)); - CHECK(j[4] == json("string")); - CHECK(j[5] == json(42.23)); - CHECK(j[6] == json(json::object())); - CHECK(j[7] == json({1, 2, 3})); - - CHECK(j_const[0] == json(1)); - CHECK(j_const[1] == json(1u)); - CHECK(j_const[2] == json(true)); - CHECK(j_const[3] == json(nullptr)); - CHECK(j_const[4] == json("string")); - CHECK(j_const[5] == json(42.23)); - CHECK(j_const[6] == json(json::object())); - CHECK(j_const[7] == json({1, 2, 3})); - } - - SECTION("access on non-array type") - { - SECTION("null") - { - SECTION("standard tests") - { - json j_nonarray(json::value_t::null); - const json j_nonarray_const(j_nonarray); - CHECK_NOTHROW(j_nonarray[0]); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with null"); - } - - SECTION("implicit transformation to properly filled array") - { - json j_nonarray; - j_nonarray[3] = 42; - CHECK(j_nonarray == json({nullptr, nullptr, nullptr, 42})); - } - } - - SECTION("boolean") - { - json j_nonarray(json::value_t::boolean); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with boolean"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with boolean"); - } - - SECTION("string") - { - json j_nonarray(json::value_t::string); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with string"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with string"); - } - - SECTION("object") - { - json j_nonarray(json::value_t::object); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with object"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with object"); - } - - SECTION("number (integer)") - { - json j_nonarray(json::value_t::number_integer); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); - } - - SECTION("number (unsigned)") - { - json j_nonarray(json::value_t::number_unsigned); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); - } - - SECTION("number (floating-point)") - { - json j_nonarray(json::value_t::number_float); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); - } - } - } - - SECTION("remove specified element") - { - SECTION("remove element by index") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(0); - CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(1); - CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(2); - CHECK(jarray == json({1, 1u, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(3); - CHECK(jarray == json({1, 1u, true, "string", 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(4); - CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(5); - CHECK(jarray == json({1, 1u, true, nullptr, "string", json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(6); - CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(7); - CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object()})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - CHECK_THROWS_AS(jarray.erase(8), std::out_of_range); - CHECK_THROWS_WITH(jarray.erase(8), "array index 8 is out of range"); - } - } - - SECTION("remove element by iterator") - { - SECTION("erase(begin())") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it2 = jarray.erase(jarray.begin()); - CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(1u)); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it2 = jarray.erase(jarray.cbegin()); - CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(1u)); - } - } - - SECTION("erase(begin(), end())") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it2 = jarray.erase(jarray.begin(), jarray.end()); - CHECK(jarray == json::array()); - CHECK(it2 == jarray.end()); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cend()); - CHECK(jarray == json::array()); - CHECK(it2 == jarray.cend()); - } - } - - SECTION("erase(begin(), begin())") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it2 = jarray.erase(jarray.begin(), jarray.begin()); - CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(1)); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cbegin()); - CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(1)); - } - } - - SECTION("erase at offset") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it = jarray.begin() + 4; - json::iterator it2 = jarray.erase(it); - CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(42.23)); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it = jarray.cbegin() + 4; - json::const_iterator it2 = jarray.erase(it); - CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(42.23)); - } - } - - SECTION("erase subrange") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it2 = jarray.erase(jarray.begin() + 3, jarray.begin() + 6); - CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); - CHECK(*it2 == json::object()); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it2 = jarray.erase(jarray.cbegin() + 3, jarray.cbegin() + 6); - CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); - CHECK(*it2 == json::object()); - } - } - - SECTION("different arrays") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json jarray2 = {"foo", "bar"}; - CHECK_THROWS_AS(jarray.erase(jarray2.begin()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray.end()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray2.end()), std::domain_error); - - CHECK_THROWS_WITH(jarray.erase(jarray2.begin()), "iterator does not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray.begin(), jarray2.end()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray.end()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray2.end()), - "iterators do not fit current value"); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json jarray2 = {"foo", "bar"}; - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), std::domain_error); - - CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin()), "iterator does not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray.cbegin(), jarray2.cend()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray.cend()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray2.cend()), - "iterators do not fit current value"); - } - } - } - - SECTION("remove element by index in non-array type") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with null"); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with boolean"); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with string"); - } - - SECTION("object") - { - json j_nonobject(json::value_t::object); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with object"); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); - } - - SECTION("number (unsigned)") - { - json j_nonobject(json::value_t::number_unsigned); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); - } - } - } - } - - SECTION("object") - { - json j = {{"integer", 1}, {"unsigned", 1u}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", json::object()}, {"array", {1, 2, 3}}}; - const json j_const = j; - - SECTION("access specified element with bounds checking") - { - SECTION("access within bounds") - { - CHECK(j.at("integer") == json(1)); - CHECK(j.at("unsigned") == json(1u)); - CHECK(j.at("boolean") == json(true)); - CHECK(j.at("null") == json(nullptr)); - CHECK(j.at("string") == json("hello world")); - CHECK(j.at("floating") == json(42.23)); - CHECK(j.at("object") == json(json::object())); - CHECK(j.at("array") == json({1, 2, 3})); - - CHECK(j_const.at("integer") == json(1)); - CHECK(j_const.at("unsigned") == json(1u)); - CHECK(j_const.at("boolean") == json(true)); - CHECK(j_const.at("null") == json(nullptr)); - CHECK(j_const.at("string") == json("hello world")); - CHECK(j_const.at("floating") == json(42.23)); - CHECK(j_const.at("object") == json(json::object())); - CHECK(j_const.at("array") == json({1, 2, 3})); - } - - SECTION("access outside bounds") - { - CHECK_THROWS_AS(j.at("foo"), std::out_of_range); - CHECK_THROWS_AS(j_const.at("foo"), std::out_of_range); - CHECK_THROWS_WITH(j.at("foo"), "key 'foo' not found"); - CHECK_THROWS_WITH(j_const.at("foo"), "key 'foo' not found"); - } - - SECTION("access on non-object type") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with null"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with null"); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with boolean"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with boolean"); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with string"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with string"); - } - - SECTION("array") - { - json j_nonobject(json::value_t::array); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with array"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with array"); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); - } - - SECTION("number (unsigned)") - { - json j_nonobject(json::value_t::number_unsigned); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); - } - } - } - - SECTION("access specified element with default value") - { - SECTION("given a key") - { - SECTION("access existing value") - { - CHECK(j.value("integer", 2) == 1); - CHECK(j.value("integer", 1.0) == Approx(1)); - CHECK(j.value("unsigned", 2) == 1u); - CHECK(j.value("unsigned", 1.0) == Approx(1u)); - CHECK(j.value("null", json(1)) == json()); - CHECK(j.value("boolean", false) == true); - CHECK(j.value("string", "bar") == "hello world"); - CHECK(j.value("string", std::string("bar")) == "hello world"); - CHECK(j.value("floating", 12.34) == Approx(42.23)); - CHECK(j.value("floating", 12) == 42); - CHECK(j.value("object", json({{"foo", "bar"}})) == json(json::object())); - CHECK(j.value("array", json({10, 100})) == json({1, 2, 3})); - - CHECK(j_const.value("integer", 2) == 1); - CHECK(j_const.value("integer", 1.0) == Approx(1)); - CHECK(j_const.value("unsigned", 2) == 1u); - CHECK(j_const.value("unsigned", 1.0) == Approx(1u)); - CHECK(j_const.value("boolean", false) == true); - CHECK(j_const.value("string", "bar") == "hello world"); - CHECK(j_const.value("string", std::string("bar")) == "hello world"); - CHECK(j_const.value("floating", 12.34) == Approx(42.23)); - CHECK(j_const.value("floating", 12) == 42); - CHECK(j_const.value("object", json({{"foo", "bar"}})) == json(json::object())); - CHECK(j_const.value("array", json({10, 100})) == json({1, 2, 3})); - } - - SECTION("access non-existing value") - { - CHECK(j.value("_", 2) == 2); - CHECK(j.value("_", 2u) == 2u); - CHECK(j.value("_", false) == false); - CHECK(j.value("_", "bar") == "bar"); - CHECK(j.value("_", 12.34) == Approx(12.34)); - CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); - CHECK(j.value("_", json({10, 100})) == json({10, 100})); - - CHECK(j_const.value("_", 2) == 2); - CHECK(j_const.value("_", 2u) == 2u); - CHECK(j_const.value("_", false) == false); - CHECK(j_const.value("_", "bar") == "bar"); - CHECK(j_const.value("_", 12.34) == Approx(12.34)); - CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); - CHECK(j_const.value("_", json({10, 100})) == json({10, 100})); - } - - SECTION("access on non-object type") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with null"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with null"); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with boolean"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with boolean"); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with string"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with string"); - } - - SECTION("array") - { - json j_nonobject(json::value_t::array); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with array"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with array"); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); - } - - SECTION("number (unsigned)") - { - json j_nonobject(json::value_t::number_unsigned); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); - } - } - } - - SECTION("given a JSON pointer") - { - SECTION("access existing value") - { - CHECK(j.value("/integer"_json_pointer, 2) == 1); - CHECK(j.value("/integer"_json_pointer, 1.0) == Approx(1)); - CHECK(j.value("/unsigned"_json_pointer, 2) == 1u); - CHECK(j.value("/unsigned"_json_pointer, 1.0) == Approx(1u)); - CHECK(j.value("/null"_json_pointer, json(1)) == json()); - CHECK(j.value("/boolean"_json_pointer, false) == true); - CHECK(j.value("/string"_json_pointer, "bar") == "hello world"); - CHECK(j.value("/string"_json_pointer, std::string("bar")) == "hello world"); - CHECK(j.value("/floating"_json_pointer, 12.34) == Approx(42.23)); - CHECK(j.value("/floating"_json_pointer, 12) == 42); - CHECK(j.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object())); - CHECK(j.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3})); - - CHECK(j_const.value("/integer"_json_pointer, 2) == 1); - CHECK(j_const.value("/integer"_json_pointer, 1.0) == Approx(1)); - CHECK(j_const.value("/unsigned"_json_pointer, 2) == 1u); - CHECK(j_const.value("/unsigned"_json_pointer, 1.0) == Approx(1u)); - CHECK(j_const.value("/boolean"_json_pointer, false) == true); - CHECK(j_const.value("/string"_json_pointer, "bar") == "hello world"); - CHECK(j_const.value("/string"_json_pointer, std::string("bar")) == "hello world"); - CHECK(j_const.value("/floating"_json_pointer, 12.34) == Approx(42.23)); - CHECK(j_const.value("/floating"_json_pointer, 12) == 42); - CHECK(j_const.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object())); - CHECK(j_const.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3})); - } - - SECTION("access non-existing value") - { - CHECK(j.value("/not/existing"_json_pointer, 2) == 2); - CHECK(j.value("/not/existing"_json_pointer, 2u) == 2u); - CHECK(j.value("/not/existing"_json_pointer, false) == false); - CHECK(j.value("/not/existing"_json_pointer, "bar") == "bar"); - CHECK(j.value("/not/existing"_json_pointer, 12.34) == Approx(12.34)); - CHECK(j.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}})); - CHECK(j.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100})); - - CHECK(j_const.value("/not/existing"_json_pointer, 2) == 2); - CHECK(j_const.value("/not/existing"_json_pointer, 2u) == 2u); - CHECK(j_const.value("/not/existing"_json_pointer, false) == false); - CHECK(j_const.value("/not/existing"_json_pointer, "bar") == "bar"); - CHECK(j_const.value("/not/existing"_json_pointer, 12.34) == Approx(12.34)); - CHECK(j_const.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}})); - CHECK(j_const.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100})); - } - - SECTION("access on non-object type") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with null"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with null"); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with boolean"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with boolean"); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with string"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with string"); - } - - SECTION("array") - { - json j_nonobject(json::value_t::array); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with array"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with array"); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with number"); - } - - SECTION("number (unsigned)") - { - json j_nonobject(json::value_t::number_unsigned); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with number"); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with number"); - } - } - } - } - - SECTION("front and back") - { - // "array" is the smallest key - CHECK(j.front() == json({1, 2, 3})); - CHECK(j_const.front() == json({1, 2, 3})); - // "unsigned" is the largest key - CHECK(j.back() == json(1u)); - CHECK(j_const.back() == json(1u)); - } - - SECTION("access specified element") - { - SECTION("access within bounds") - { - CHECK(j["integer"] == json(1)); - CHECK(j[json::object_t::key_type("integer")] == j["integer"]); - - CHECK(j["unsigned"] == json(1u)); - CHECK(j[json::object_t::key_type("unsigned")] == j["unsigned"]); - - CHECK(j["boolean"] == json(true)); - CHECK(j[json::object_t::key_type("boolean")] == j["boolean"]); - - CHECK(j["null"] == json(nullptr)); - CHECK(j[json::object_t::key_type("null")] == j["null"]); - - CHECK(j["string"] == json("hello world")); - CHECK(j[json::object_t::key_type("string")] == j["string"]); - - CHECK(j["floating"] == json(42.23)); - CHECK(j[json::object_t::key_type("floating")] == j["floating"]); - - CHECK(j["object"] == json(json::object())); - CHECK(j[json::object_t::key_type("object")] == j["object"]); - - CHECK(j["array"] == json({1, 2, 3})); - CHECK(j[json::object_t::key_type("array")] == j["array"]); - - CHECK(j_const["integer"] == json(1)); - CHECK(j_const[json::object_t::key_type("integer")] == j["integer"]); - - CHECK(j_const["boolean"] == json(true)); - CHECK(j_const[json::object_t::key_type("boolean")] == j["boolean"]); - - CHECK(j_const["null"] == json(nullptr)); - CHECK(j_const[json::object_t::key_type("null")] == j["null"]); - - CHECK(j_const["string"] == json("hello world")); - CHECK(j_const[json::object_t::key_type("string")] == j["string"]); - - CHECK(j_const["floating"] == json(42.23)); - CHECK(j_const[json::object_t::key_type("floating")] == j["floating"]); - - CHECK(j_const["object"] == json(json::object())); - CHECK(j_const[json::object_t::key_type("object")] == j["object"]); - - CHECK(j_const["array"] == json({1, 2, 3})); - CHECK(j_const[json::object_t::key_type("array")] == j["array"]); - } - - SECTION("access on non-object type") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - json j_nonobject2(json::value_t::null); - const json j_const_nonobject(j_nonobject); - CHECK_NOTHROW(j_nonobject["foo"]); - CHECK_NOTHROW(j_nonobject2[json::object_t::key_type("foo")]); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with null"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with null"); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with boolean"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with boolean"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with boolean"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with boolean"); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with string"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with string"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with string"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with string"); - } - - SECTION("array") - { - json j_nonobject(json::value_t::array); - const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with array"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with array"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with array"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with array"); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - } - - SECTION("number (unsigned)") - { - json j_nonobject(json::value_t::number_unsigned); - const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - } - } - } - - SECTION("remove specified element") - { - SECTION("remove element by key") - { - CHECK(j.find("integer") != j.end()); - CHECK(j.erase("integer") == 1); - CHECK(j.find("integer") == j.end()); - CHECK(j.erase("integer") == 0); - - CHECK(j.find("unsigned") != j.end()); - CHECK(j.erase("unsigned") == 1); - CHECK(j.find("unsigned") == j.end()); - CHECK(j.erase("unsigned") == 0); - - CHECK(j.find("boolean") != j.end()); - CHECK(j.erase("boolean") == 1); - CHECK(j.find("boolean") == j.end()); - CHECK(j.erase("boolean") == 0); - - CHECK(j.find("null") != j.end()); - CHECK(j.erase("null") == 1); - CHECK(j.find("null") == j.end()); - CHECK(j.erase("null") == 0); - - CHECK(j.find("string") != j.end()); - CHECK(j.erase("string") == 1); - CHECK(j.find("string") == j.end()); - CHECK(j.erase("string") == 0); - - CHECK(j.find("floating") != j.end()); - CHECK(j.erase("floating") == 1); - CHECK(j.find("floating") == j.end()); - CHECK(j.erase("floating") == 0); - - CHECK(j.find("object") != j.end()); - CHECK(j.erase("object") == 1); - CHECK(j.find("object") == j.end()); - CHECK(j.erase("object") == 0); - - CHECK(j.find("array") != j.end()); - CHECK(j.erase("array") == 1); - CHECK(j.find("array") == j.end()); - CHECK(j.erase("array") == 0); - } - - SECTION("remove element by iterator") - { - SECTION("erase(begin())") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::iterator it2 = jobject.erase(jobject.begin()); - CHECK(jobject == json({{"b", 1}, {"c", 17u}})); - CHECK(*it2 == json(1)); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::const_iterator it2 = jobject.erase(jobject.cbegin()); - CHECK(jobject == json({{"b", 1}, {"c", 17u}})); - CHECK(*it2 == json(1)); - } - } - - SECTION("erase(begin(), end())") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::iterator it2 = jobject.erase(jobject.begin(), jobject.end()); - CHECK(jobject == json::object()); - CHECK(it2 == jobject.end()); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cend()); - CHECK(jobject == json::object()); - CHECK(it2 == jobject.cend()); - } - } - - SECTION("erase(begin(), begin())") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::iterator it2 = jobject.erase(jobject.begin(), jobject.begin()); - CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17u}})); - CHECK(*it2 == json("a")); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cbegin()); - CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17u}})); - CHECK(*it2 == json("a")); - } - } - - SECTION("erase at offset") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::iterator it = jobject.find("b"); - json::iterator it2 = jobject.erase(it); - CHECK(jobject == json({{"a", "a"}, {"c", 17u}})); - CHECK(*it2 == json(17)); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::const_iterator it = jobject.find("b"); - json::const_iterator it2 = jobject.erase(it); - CHECK(jobject == json({{"a", "a"}, {"c", 17u}})); - CHECK(*it2 == json(17)); - } - } - - SECTION("erase subrange") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; - json::iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e")); - CHECK(jobject == json({{"a", "a"}, {"e", true}})); - CHECK(*it2 == json(true)); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; - json::const_iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e")); - CHECK(jobject == json({{"a", "a"}, {"e", true}})); - CHECK(*it2 == json(true)); - } - } - - SECTION("different objects") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; - json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(jobject.erase(jobject2.begin()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject.begin(), jobject2.end()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject.end()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject2.end()), std::domain_error); - CHECK_THROWS_WITH(jobject.erase(jobject2.begin()), "iterator does not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject.begin(), jobject2.end()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject.end()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject2.end()), - "iterators do not fit current value"); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; - json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(jobject.erase(jobject2.cbegin()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject2.cend()), std::domain_error); - CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin()), "iterator does not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject.cbegin(), jobject2.cend()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject.cend()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject2.cend()), - "iterators do not fit current value"); - } - } - } - - SECTION("remove element by key in non-object type") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with null"); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with boolean"); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with string"); - } - - SECTION("array") - { - json j_nonobject(json::value_t::array); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with array"); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number"); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number"); - } - } - } - - SECTION("find an element in an object") - { - SECTION("existing element") - { - for (auto key : - {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" - }) - { - CHECK(j.find(key) != j.end()); - CHECK(*j.find(key) == j.at(key)); - CHECK(j_const.find(key) != j_const.end()); - CHECK(*j_const.find(key) == j_const.at(key)); - } - } - - SECTION("nonexisting element") - { - CHECK(j.find("foo") == j.end()); - CHECK(j_const.find("foo") == j_const.end()); - } - - SECTION("all types") - { - SECTION("null") - { - json j_nonarray(json::value_t::null); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - - SECTION("string") - { - json j_nonarray(json::value_t::string); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - - SECTION("object") - { - json j_nonarray(json::value_t::object); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - - SECTION("array") - { - json j_nonarray(json::value_t::array); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - - SECTION("boolean") - { - json j_nonarray(json::value_t::boolean); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - - SECTION("number (integer)") - { - json j_nonarray(json::value_t::number_integer); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - - SECTION("number (unsigned)") - { - json j_nonarray(json::value_t::number_unsigned); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - - SECTION("number (floating-point)") - { - json j_nonarray(json::value_t::number_float); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - } - } - - SECTION("count keys in an object") - { - SECTION("existing element") - { - for (auto key : - {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" - }) - { - CHECK(j.count(key) == 1); - CHECK(j_const.count(key) == 1); - } - } - - SECTION("nonexisting element") - { - CHECK(j.count("foo") == 0); - CHECK(j_const.count("foo") == 0); - } - - SECTION("all types") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - - SECTION("object") - { - json j_nonobject(json::value_t::object); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - - SECTION("array") - { - json j_nonobject(json::value_t::array); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - - SECTION("number (unsigned)") - { - json j_nonobject(json::value_t::number_unsigned); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - } - } - } - - SECTION("other values") - { - SECTION("front and back") - { - SECTION("null") - { - { - json j; - CHECK_THROWS_AS(j.front(), std::out_of_range); - CHECK_THROWS_AS(j.back(), std::out_of_range); - CHECK_THROWS_WITH(j.front(), "cannot get value"); - CHECK_THROWS_WITH(j.back(), "cannot get value"); - } - { - const json j{}; - CHECK_THROWS_AS(j.front(), std::out_of_range); - CHECK_THROWS_AS(j.back(), std::out_of_range); - CHECK_THROWS_WITH(j.front(), "cannot get value"); - CHECK_THROWS_WITH(j.back(), "cannot get value"); - } - } - - SECTION("string") - { - { - json j = "foo"; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = "bar"; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = true; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = 17; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = 17u; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = 23.42; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - } - - SECTION("erase with one valid iterator") - { - SECTION("null") - { - { - json j; - CHECK_THROWS_AS(j.erase(j.begin()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); - } - { - json j; - CHECK_THROWS_AS(j.erase(j.cbegin()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); - } - } - - SECTION("string") - { - { - json j = "foo"; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = "bar"; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = true; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 17; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 17u; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 23.42; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - } - - SECTION("erase with one invalid iterator") - { - SECTION("string") - { - { - json j = "foo"; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = "bar"; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = true; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = 17; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = 17u; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = 23.42; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - } - - SECTION("erase with two valid iterators") - { - SECTION("null") - { - { - json j; - CHECK_THROWS_AS(j.erase(j.begin(), j.end()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.begin(), j.end()), "cannot use erase() with null"); - } - { - json j; - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cend()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cend()), "cannot use erase() with null"); - } - } - - SECTION("string") - { - { - json j = "foo"; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = "bar"; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = true; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 17; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 17u; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 23.42; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - } - - SECTION("erase with two invalid iterators") - { - SECTION("string") - { - { - json j = "foo"; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = "bar"; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = true; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 17; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 17u; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 23.42; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - } - } -} - -TEST_CASE("iterators") -{ - SECTION("basic behavior") - { - SECTION("uninitialized") - { - json::iterator it; - CHECK(it.m_object == nullptr); - - json::const_iterator cit; - CHECK(cit.m_object == nullptr); - } - - SECTION("boolean") - { - json j = true; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it != j.end()); - CHECK(*it == j); - - it++; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - it--; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - - ++it; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - --it; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - } - - SECTION("const json + begin/end") - { - json::const_iterator it = j_const.begin(); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - it--; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - --it; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it = j.cbegin(); - CHECK(it != j.cend()); - CHECK(*it == j); - - it++; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - it--; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - --it; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it = j_const.cbegin(); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - it--; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - --it; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it != j.rend()); - CHECK(*it == j); - - it++; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - it--; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - --it; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it != j.crend()); - CHECK(*it == j); - - it++; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - it--; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - --it; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - it--; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - --it; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK(it.value() == json(true)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK(cit.value() == json(true)); - - auto rit = j.rend(); - auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); - } - } - - SECTION("string") - { - json j = "hello world"; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it != j.end()); - CHECK(*it == j); - - it++; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - it--; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - - ++it; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - --it; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - } - - SECTION("const json + begin/end") - { - json::const_iterator it = j_const.begin(); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - it--; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - --it; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it = j.cbegin(); - CHECK(it != j.cend()); - CHECK(*it == j); - - it++; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - it--; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - --it; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it = j_const.cbegin(); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - it--; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - --it; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it != j.rend()); - CHECK(*it == j); - - it++; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - it--; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - --it; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it != j.crend()); - CHECK(*it == j); - - it++; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - it--; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - --it; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - it--; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - --it; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK(it.value() == json("hello world")); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK(cit.value() == json("hello world")); - - auto rit = j.rend(); - auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); - } - } - - SECTION("array") - { - json j = {1, 2, 3}; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it_begin = j.begin(); - json::iterator it_end = j.end(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[0]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[2]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + begin/end") - { - json::const_iterator it_begin = j_const.begin(); - json::const_iterator it_end = j_const.end(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j_const[0]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const[2]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it_begin = j.cbegin(); - json::const_iterator it_end = j.cend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[0]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[2]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it_begin = j_const.cbegin(); - json::const_iterator it_end = j_const.cend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[0]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[2]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it_begin = j.rbegin(); - json::reverse_iterator it_end = j.rend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[2]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[0]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it_begin = j.crbegin(); - json::const_reverse_iterator it_end = j.crend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[2]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[0]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it_begin = j_const.crbegin(); - json::const_reverse_iterator it_end = j_const.crend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[2]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[0]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK(it.value() == json(1)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK(cit.value() == json(1)); - } - } - - SECTION("object") - { - json j = {{"A", 1}, {"B", 2}, {"C", 3}}; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it_begin = j.begin(); - json::iterator it_end = j.end(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["A"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["C"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + begin/end") - { - json::const_iterator it_begin = j_const.begin(); - json::const_iterator it_end = j_const.end(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j_const["A"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const["C"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it_begin = j.cbegin(); - json::const_iterator it_end = j.cend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["A"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["C"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it_begin = j_const.cbegin(); - json::const_iterator it_end = j_const.cend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j_const["A"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const["C"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it_begin = j.rbegin(); - json::reverse_iterator it_end = j.rend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["C"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["A"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it_begin = j.crbegin(); - json::const_reverse_iterator it_end = j.crend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["C"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["A"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it_begin = j_const.crbegin(); - json::const_reverse_iterator it_end = j_const.crend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["C"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["A"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK(it.key() == "A"); - CHECK(it.value() == json(1)); - CHECK(cit.key() == "A"); - CHECK(cit.value() == json(1)); - } - } - - SECTION("number (integer)") - { - json j = 23; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it != j.end()); - CHECK(*it == j); - - it++; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - it--; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - - ++it; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - --it; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - } - - SECTION("const json + begin/end") - { - json::const_iterator it = j_const.begin(); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - it--; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - --it; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it = j.cbegin(); - CHECK(it != j.cend()); - CHECK(*it == j); - - it++; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - it--; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - --it; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it = j_const.cbegin(); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - it--; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - --it; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it != j.rend()); - CHECK(*it == j); - - it++; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - it--; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - --it; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it != j.crend()); - CHECK(*it == j); - - it++; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - it--; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - --it; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - it--; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - --it; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK(it.value() == json(23)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK(cit.value() == json(23)); - - auto rit = j.rend(); - auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); - } - } - - SECTION("number (unsigned)") - { - json j = 23u; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it != j.end()); - CHECK(*it == j); - - it++; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - it--; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - - ++it; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - --it; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - } - - SECTION("const json + begin/end") - { - json::const_iterator it = j_const.begin(); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - it--; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - --it; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it = j.cbegin(); - CHECK(it != j.cend()); - CHECK(*it == j); - - it++; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - it--; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - --it; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it = j_const.cbegin(); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - it--; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - --it; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it != j.rend()); - CHECK(*it == j); - - it++; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - it--; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - --it; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it != j.crend()); - CHECK(*it == j); - - it++; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - it--; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - --it; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - it--; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - --it; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK(it.value() == json(23)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK(cit.value() == json(23)); - - auto rit = j.rend(); - auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); - } - } - - SECTION("number (float)") - { - json j = 23.42; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it != j.end()); - CHECK(*it == j); - - it++; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - it--; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - - ++it; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - --it; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - } - - SECTION("const json + begin/end") - { - json::const_iterator it = j_const.begin(); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - it--; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - --it; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it = j.cbegin(); - CHECK(it != j.cend()); - CHECK(*it == j); - - it++; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - it--; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - --it; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it = j_const.cbegin(); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - it--; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - --it; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it != j.rend()); - CHECK(*it == j); - - it++; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - it--; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - --it; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it != j.crend()); - CHECK(*it == j); - - it++; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - it--; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - --it; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - it--; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - --it; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK(it.value() == json(23.42)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK(cit.value() == json(23.42)); - - auto rit = j.rend(); - auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); - } - } - - SECTION("null") - { - json j = nullptr; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it == j.end()); - } - - SECTION("const json + begin/end") - { - json::const_iterator it_begin = j_const.begin(); - json::const_iterator it_end = j_const.end(); - CHECK(it_begin == it_end); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it_begin = j.cbegin(); - json::const_iterator it_end = j.cend(); - CHECK(it_begin == it_end); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it_begin = j_const.cbegin(); - json::const_iterator it_end = j_const.cend(); - CHECK(it_begin == it_end); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it == j.rend()); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it == j.crend()); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it == j_const.crend()); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_AS(it.value(), std::out_of_range); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_AS(cit.value(), std::out_of_range); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(it.value(), "cannot get value"); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(cit.value(), "cannot get value"); - - auto rit = j.rend(); - auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); - } - } - } - - SECTION("iterator comparisons") - { - json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; - - for (json& j : j_values) - { - auto it1 = j.begin(); - auto it2 = j.begin(); - auto it3 = j.begin(); - ++it2; - ++it3; - ++it3; - auto it1_c = j.cbegin(); - auto it2_c = j.cbegin(); - auto it3_c = j.cbegin(); - ++it2_c; - ++it3_c; - ++it3_c; - - // comparison: equal - { - CHECK(it1 == it1); - CHECK(not (it1 == it2)); - CHECK(not (it1 == it3)); - CHECK(not (it2 == it3)); - CHECK(it1_c == it1_c); - CHECK(not (it1_c == it2_c)); - CHECK(not (it1_c == it3_c)); - CHECK(not (it2_c == it3_c)); - } - - // comparison: not equal - { - // check definition - CHECK( (it1 != it1) == not(it1 == it1) ); - CHECK( (it1 != it2) == not(it1 == it2) ); - CHECK( (it1 != it3) == not(it1 == it3) ); - CHECK( (it2 != it3) == not(it2 == it3) ); - CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); - CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); - CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); - CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); - } - - // comparison: smaller - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 < it1, std::domain_error); - CHECK_THROWS_AS(it1 < it2, std::domain_error); - CHECK_THROWS_AS(it2 < it3, std::domain_error); - CHECK_THROWS_AS(it1 < it3, std::domain_error); - CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); - } - else - { - CHECK(not (it1 < it1)); - CHECK(it1 < it2); - CHECK(it1 < it3); - CHECK(it2 < it3); - CHECK(not (it1_c < it1_c)); - CHECK(it1_c < it2_c); - CHECK(it1_c < it3_c); - CHECK(it2_c < it3_c); - } - } - - // comparison: less than or equal - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 <= it1, std::domain_error); - CHECK_THROWS_AS(it1 <= it2, std::domain_error); - CHECK_THROWS_AS(it2 <= it3, std::domain_error); - CHECK_THROWS_AS(it1 <= it3, std::domain_error); - CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 <= it1) == not(it1 < it1) ); - CHECK( (it1 <= it2) == not(it2 < it1) ); - CHECK( (it1 <= it3) == not(it3 < it1) ); - CHECK( (it2 <= it3) == not(it3 < it2) ); - CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); - CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); - CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); - CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); - } - } - - // comparison: greater than - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 > it1, std::domain_error); - CHECK_THROWS_AS(it1 > it2, std::domain_error); - CHECK_THROWS_AS(it2 > it3, std::domain_error); - CHECK_THROWS_AS(it1 > it3, std::domain_error); - CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 > it1) == (it1 < it1) ); - CHECK( (it1 > it2) == (it2 < it1) ); - CHECK( (it1 > it3) == (it3 < it1) ); - CHECK( (it2 > it3) == (it3 < it2) ); - CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); - CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); - CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); - CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); - } - } - - // comparison: greater than or equal - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 >= it1, std::domain_error); - CHECK_THROWS_AS(it1 >= it2, std::domain_error); - CHECK_THROWS_AS(it2 >= it3, std::domain_error); - CHECK_THROWS_AS(it1 >= it3, std::domain_error); - CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 >= it1) == not(it1 < it1) ); - CHECK( (it1 >= it2) == not(it1 < it2) ); - CHECK( (it1 >= it3) == not(it1 < it3) ); - CHECK( (it2 >= it3) == not(it2 < it3) ); - CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); - CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); - CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); - CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); - } - } - } - - // check exceptions if different objects are compared - for (auto j : j_values) - { - for (auto k : j_values) - { - if (j != k) - { - CHECK_THROWS_AS(j.begin() == k.begin(), std::domain_error); - CHECK_THROWS_AS(j.cbegin() == k.cbegin(), std::domain_error); - CHECK_THROWS_WITH(j.begin() == k.begin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "cannot compare iterators of different containers"); - - CHECK_THROWS_AS(j.begin() < k.begin(), std::domain_error); - CHECK_THROWS_AS(j.cbegin() < k.cbegin(), std::domain_error); - CHECK_THROWS_WITH(j.begin() < k.begin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "cannot compare iterators of different containers"); - } - } - } - } - - SECTION("iterator arithmetic") - { - json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j_array = {1, 2, 3, 4, 5, 6}; - json j_null = nullptr; - json j_value = 42; - - SECTION("addition and subtraction") - { - SECTION("object") - { - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); - } - } - - SECTION("array") - { - { - auto it = j_array.begin(); - it += 3; - CHECK((j_array.begin() + 3) == it); - CHECK((it - 3) == j_array.begin()); - CHECK((it - j_array.begin()) == 3); - CHECK(*it == json(4)); - it -= 2; - CHECK(*it == json(2)); - } - { - auto it = j_array.cbegin(); - it += 3; - CHECK((j_array.cbegin() + 3) == it); - CHECK((it - 3) == j_array.cbegin()); - CHECK((it - j_array.cbegin()) == 3); - CHECK(*it == json(4)); - it -= 2; - CHECK(*it == json(2)); - } - } - - SECTION("null") - { - { - auto it = j_null.begin(); - it += 3; - CHECK((j_null.begin() + 3) == it); - CHECK((it - 3) == j_null.begin()); - CHECK((it - j_null.begin()) == 3); - CHECK(it != j_null.end()); - it -= 3; - CHECK(it == j_null.end()); - } - { - auto it = j_null.cbegin(); - it += 3; - CHECK((j_null.cbegin() + 3) == it); - CHECK((it - 3) == j_null.cbegin()); - CHECK((it - j_null.cbegin()) == 3); - CHECK(it != j_null.cend()); - it -= 3; - CHECK(it == j_null.cend()); - } - } - - SECTION("value") - { - { - auto it = j_value.begin(); - it += 3; - CHECK((j_value.begin() + 3) == it); - CHECK((it - 3) == j_value.begin()); - CHECK((it - j_value.begin()) == 3); - CHECK(it != j_value.end()); - it -= 3; - CHECK(*it == json(42)); - } - { - auto it = j_value.cbegin(); - it += 3; - CHECK((j_value.cbegin() + 3) == it); - CHECK((it - 3) == j_value.cbegin()); - CHECK((it - j_value.cbegin()) == 3); - CHECK(it != j_value.cend()); - it -= 3; - CHECK(*it == json(42)); - } - } - } - - SECTION("subscript operator") - { - SECTION("object") - { - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); - } - } - - SECTION("array") - { - { - auto it = j_array.begin(); - CHECK(it[0] == json(1)); - CHECK(it[1] == json(2)); - CHECK(it[2] == json(3)); - CHECK(it[3] == json(4)); - CHECK(it[4] == json(5)); - CHECK(it[5] == json(6)); - } - { - auto it = j_array.cbegin(); - CHECK(it[0] == json(1)); - CHECK(it[1] == json(2)); - CHECK(it[2] == json(3)); - CHECK(it[3] == json(4)); - CHECK(it[4] == json(5)); - CHECK(it[5] == json(6)); - } - } - - SECTION("null") - { - { - auto it = j_null.begin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - { - auto it = j_null.cbegin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - } - - SECTION("value") - { - { - auto it = j_value.begin(); - CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - { - auto it = j_value.cbegin(); - CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - } - } - } - - SECTION("reverse iterator comparisons") - { - json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; - - for (json& j : j_values) - { - auto it1 = j.rbegin(); - auto it2 = j.rbegin(); - auto it3 = j.rbegin(); - ++it2; - ++it3; - ++it3; - auto it1_c = j.crbegin(); - auto it2_c = j.crbegin(); - auto it3_c = j.crbegin(); - ++it2_c; - ++it3_c; - ++it3_c; - - // comparison: equal - { - CHECK(it1 == it1); - CHECK(not (it1 == it2)); - CHECK(not (it1 == it3)); - CHECK(not (it2 == it3)); - CHECK(it1_c == it1_c); - CHECK(not (it1_c == it2_c)); - CHECK(not (it1_c == it3_c)); - CHECK(not (it2_c == it3_c)); - } - - // comparison: not equal - { - // check definition - CHECK( (it1 != it1) == not(it1 == it1) ); - CHECK( (it1 != it2) == not(it1 == it2) ); - CHECK( (it1 != it3) == not(it1 == it3) ); - CHECK( (it2 != it3) == not(it2 == it3) ); - CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); - CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); - CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); - CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); - } - - // comparison: smaller - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 < it1, std::domain_error); - CHECK_THROWS_AS(it1 < it2, std::domain_error); - CHECK_THROWS_AS(it2 < it3, std::domain_error); - CHECK_THROWS_AS(it1 < it3, std::domain_error); - CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); - } - else - { - CHECK(not (it1 < it1)); - CHECK(it1 < it2); - CHECK(it1 < it3); - CHECK(it2 < it3); - CHECK(not (it1_c < it1_c)); - CHECK(it1_c < it2_c); - CHECK(it1_c < it3_c); - CHECK(it2_c < it3_c); - } - } - - // comparison: less than or equal - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 <= it1, std::domain_error); - CHECK_THROWS_AS(it1 <= it2, std::domain_error); - CHECK_THROWS_AS(it2 <= it3, std::domain_error); - CHECK_THROWS_AS(it1 <= it3, std::domain_error); - CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 <= it1) == not(it1 < it1) ); - CHECK( (it1 <= it2) == not(it2 < it1) ); - CHECK( (it1 <= it3) == not(it3 < it1) ); - CHECK( (it2 <= it3) == not(it3 < it2) ); - CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); - CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); - CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); - CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); - } - } - - // comparison: greater than - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 > it1, std::domain_error); - CHECK_THROWS_AS(it1 > it2, std::domain_error); - CHECK_THROWS_AS(it2 > it3, std::domain_error); - CHECK_THROWS_AS(it1 > it3, std::domain_error); - CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 > it1) == (it1 < it1) ); - CHECK( (it1 > it2) == (it2 < it1) ); - CHECK( (it1 > it3) == (it3 < it1) ); - CHECK( (it2 > it3) == (it3 < it2) ); - CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); - CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); - CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); - CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); - } - } - - // comparison: greater than or equal - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 >= it1, std::domain_error); - CHECK_THROWS_AS(it1 >= it2, std::domain_error); - CHECK_THROWS_AS(it2 >= it3, std::domain_error); - CHECK_THROWS_AS(it1 >= it3, std::domain_error); - CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 >= it1) == not(it1 < it1) ); - CHECK( (it1 >= it2) == not(it1 < it2) ); - CHECK( (it1 >= it3) == not(it1 < it3) ); - CHECK( (it2 >= it3) == not(it2 < it3) ); - CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); - CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); - CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); - CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); - } - } - } - - // check exceptions if different objects are compared - for (auto j : j_values) - { - for (auto k : j_values) - { - if (j != k) - { - CHECK_THROWS_AS(j.rbegin() == k.rbegin(), std::domain_error); - CHECK_THROWS_AS(j.crbegin() == k.crbegin(), std::domain_error); - CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "cannot compare iterators of different containers"); - - CHECK_THROWS_AS(j.rbegin() < k.rbegin(), std::domain_error); - CHECK_THROWS_AS(j.crbegin() < k.crbegin(), std::domain_error); - CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "cannot compare iterators of different containers"); - } - } - } - } - - SECTION("reverse iterator arithmetic") - { - json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j_array = {1, 2, 3, 4, 5, 6}; - json j_null = nullptr; - json j_value = 42; - - SECTION("addition and subtraction") - { - SECTION("object") - { - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); - } - } - - SECTION("array") - { - { - auto it = j_array.rbegin(); - it += 3; - CHECK((j_array.rbegin() + 3) == it); - CHECK((it - 3) == j_array.rbegin()); - CHECK((j_array.rbegin() - it) == 3); - CHECK(*it == json(3)); - it -= 2; - CHECK(*it == json(5)); - } - { - auto it = j_array.crbegin(); - it += 3; - CHECK((j_array.crbegin() + 3) == it); - CHECK((it - 3) == j_array.crbegin()); - CHECK((j_array.crbegin() - it) == 3); - CHECK(*it == json(3)); - it -= 2; - CHECK(*it == json(5)); - } - } - - SECTION("null") - { - { - auto it = j_null.rbegin(); - it += 3; - CHECK((j_null.rbegin() + 3) == it); - CHECK((it - 3) == j_null.rbegin()); - CHECK((j_null.rbegin() - it) == 3); - CHECK(it != j_null.rend()); - it -= 3; - CHECK(it == j_null.rend()); - } - { - auto it = j_null.crbegin(); - it += 3; - CHECK((j_null.crbegin() + 3) == it); - CHECK((it - 3) == j_null.crbegin()); - CHECK((j_null.crbegin() - it) == 3); - CHECK(it != j_null.crend()); - it -= 3; - CHECK(it == j_null.crend()); - } - } - - SECTION("value") - { - { - auto it = j_value.rbegin(); - it += 3; - CHECK((j_value.rbegin() + 3) == it); - CHECK((it - 3) == j_value.rbegin()); - CHECK((j_value.rbegin() - it) == 3); - CHECK(it != j_value.rend()); - it -= 3; - CHECK(*it == json(42)); - } - { - auto it = j_value.crbegin(); - it += 3; - CHECK((j_value.crbegin() + 3) == it); - CHECK((it - 3) == j_value.crbegin()); - CHECK((j_value.crbegin() - it) == 3); - CHECK(it != j_value.crend()); - it -= 3; - CHECK(*it == json(42)); - } - } - } - - SECTION("subscript operator") - { - SECTION("object") - { - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); - } - } - - SECTION("array") - { - { - auto it = j_array.rbegin(); - CHECK(it[0] == json(6)); - CHECK(it[1] == json(5)); - CHECK(it[2] == json(4)); - CHECK(it[3] == json(3)); - CHECK(it[4] == json(2)); - CHECK(it[5] == json(1)); - } - { - auto it = j_array.crbegin(); - CHECK(it[0] == json(6)); - CHECK(it[1] == json(5)); - CHECK(it[2] == json(4)); - CHECK(it[3] == json(3)); - CHECK(it[4] == json(2)); - CHECK(it[5] == json(1)); - } - } - - SECTION("null") - { - { - auto it = j_null.rbegin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - { - auto it = j_null.crbegin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - } - - SECTION("value") - { - { - auto it = j_value.rbegin(); - CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - { - auto it = j_value.crbegin(); - CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - } - } - } -} - -TEST_CASE("capacity") -{ - SECTION("empty()") - { - SECTION("boolean") - { - json j = true; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == false); - CHECK(j_const.empty() == false); - } - - SECTION("definition of empty") - { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); - } - } - - SECTION("string") - { - json j = "hello world"; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == false); - CHECK(j_const.empty() == false); - } - - SECTION("definition of empty") - { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); - } - } - - SECTION("array") - { - SECTION("empty array") - { - json j = json::array(); - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == true); - CHECK(j_const.empty() == true); - } - - SECTION("definition of empty") - { - CHECK(j.begin() == j.end()); - CHECK(j_const.begin() == j_const.end()); - } - } - - SECTION("filled array") - { - json j = {1, 2, 3}; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == false); - CHECK(j_const.empty() == false); - } - - SECTION("definition of empty") - { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); - } - } - } - - SECTION("object") - { - SECTION("empty object") - { - json j = json::object(); - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == true); - CHECK(j_const.empty() == true); - } - - SECTION("definition of empty") - { - CHECK(j.begin() == j.end()); - CHECK(j_const.begin() == j_const.end()); - } - } - - SECTION("filled object") - { - json j = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == false); - CHECK(j_const.empty() == false); - } - - SECTION("definition of empty") - { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); - } - } - } - - SECTION("number (integer)") - { - json j = 23; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == false); - CHECK(j_const.empty() == false); - } - - SECTION("definition of empty") - { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); - } - } - - SECTION("number (unsigned)") - { - json j = 23u; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == false); - CHECK(j_const.empty() == false); - } - - SECTION("definition of empty") - { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); - } - } - - SECTION("number (float)") - { - json j = 23.42; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == false); - CHECK(j_const.empty() == false); - } - - SECTION("definition of empty") - { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); - } - } - - SECTION("null") - { - json j = nullptr; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == true); - CHECK(j_const.empty() == true); - } - - SECTION("definition of empty") - { - CHECK(j.begin() == j.end()); - CHECK(j_const.begin() == j_const.end()); - } - } - } - - SECTION("size()") - { - SECTION("boolean") - { - json j = true; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 1); - CHECK(j_const.size() == 1); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - - SECTION("string") - { - json j = "hello world"; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 1); - CHECK(j_const.size() == 1); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - - SECTION("array") - { - SECTION("empty array") - { - json j = json::array(); - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 0); - CHECK(j_const.size() == 0); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - - SECTION("filled array") - { - json j = {1, 2, 3}; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 3); - CHECK(j_const.size() == 3); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - } - - SECTION("object") - { - SECTION("empty object") - { - json j = json::object(); - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 0); - CHECK(j_const.size() == 0); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - - SECTION("filled object") - { - json j = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 3); - CHECK(j_const.size() == 3); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - } - - SECTION("number (integer)") - { - json j = 23; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 1); - CHECK(j_const.size() == 1); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - - SECTION("number (unsigned)") - { - json j = 23u; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 1); - CHECK(j_const.size() == 1); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - - SECTION("number (float)") - { - json j = 23.42; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 1); - CHECK(j_const.size() == 1); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - - SECTION("null") - { - json j = nullptr; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 0); - CHECK(j_const.size() == 0); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - } - - SECTION("max_size()") - { - SECTION("boolean") - { - json j = true; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() == 1); - CHECK(j_const.max_size() == 1); - } - } - - SECTION("string") - { - json j = "hello world"; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() == 1); - CHECK(j_const.max_size() == 1); - } - } - - SECTION("array") - { - SECTION("empty array") - { - json j = json::array(); - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() >= j.size()); - CHECK(j_const.max_size() >= j_const.size()); - } - } - - SECTION("filled array") - { - json j = {1, 2, 3}; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() >= j.size()); - CHECK(j_const.max_size() >= j_const.size()); - } - } - } - - SECTION("object") - { - SECTION("empty object") - { - json j = json::object(); - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() >= j.size()); - CHECK(j_const.max_size() >= j_const.size()); - } - } - - SECTION("filled object") - { - json j = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() >= j.size()); - CHECK(j_const.max_size() >= j_const.size()); - } - } - } - - SECTION("number (integer)") - { - json j = 23; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() == 1); - CHECK(j_const.max_size() == 1); - } - } - - SECTION("number (unsigned)") - { - json j = 23u; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() == 1); - CHECK(j_const.max_size() == 1); - } - } - - SECTION("number (float)") - { - json j = 23.42; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() == 1); - CHECK(j_const.max_size() == 1); - } - } - - SECTION("null") - { - json j = nullptr; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() == 0); - CHECK(j_const.max_size() == 0); - } - } - } -} - -TEST_CASE("modifiers") -{ - SECTION("clear()") - { - SECTION("boolean") - { - json j = true; - - j.clear(); - CHECK(j == json(json::value_t::boolean)); - } - - SECTION("string") - { - json j = "hello world"; - - j.clear(); - CHECK(j == json(json::value_t::string)); - } - - SECTION("array") - { - SECTION("empty array") - { - json j = json::array(); - - j.clear(); - CHECK(j.empty()); - CHECK(j == json(json::value_t::array)); - } - - SECTION("filled array") - { - json j = {1, 2, 3}; - - j.clear(); - CHECK(j.empty()); - CHECK(j == json(json::value_t::array)); - } - } - - SECTION("object") - { - SECTION("empty object") - { - json j = json::object(); - - j.clear(); - CHECK(j.empty()); - CHECK(j == json(json::value_t::object)); - } - - SECTION("filled object") - { - json j = {{"one", 1}, {"two", 2}, {"three", 3}}; - - j.clear(); - CHECK(j.empty()); - CHECK(j == json(json::value_t::object)); - } - } - - SECTION("number (integer)") - { - json j = 23; - - j.clear(); - CHECK(j == json(json::value_t::number_integer)); - } - - SECTION("number (unsigned)") - { - json j = 23u; - - j.clear(); - CHECK(j == json(json::value_t::number_integer)); - } - - SECTION("number (float)") - { - json j = 23.42; - - j.clear(); - CHECK(j == json(json::value_t::number_float)); - } - - SECTION("null") - { - json j = nullptr; - - j.clear(); - CHECK(j == json(json::value_t::null)); - } - } - - SECTION("push_back()") - { - SECTION("to array") - { - SECTION("json&&") - { - SECTION("null") - { - json j; - j.push_back(1); - j.push_back(2); - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 2})); - } - - SECTION("array") - { - json j = {1, 2, 3}; - j.push_back("Hello"); - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 2, 3, "Hello"})); - } - - SECTION("other type") - { - json j = 1; - CHECK_THROWS_AS(j.push_back("Hello"), std::domain_error); - CHECK_THROWS_WITH(j.push_back("Hello"), "cannot use push_back() with number"); - } - } - - SECTION("const json&") - { - SECTION("null") - { - json j; - json k(1); - j.push_back(k); - j.push_back(k); - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 1})); - } - - SECTION("array") - { - json j = {1, 2, 3}; - json k("Hello"); - j.push_back(k); - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 2, 3, "Hello"})); - } - - SECTION("other type") - { - json j = 1; - json k("Hello"); - CHECK_THROWS_AS(j.push_back(k), std::domain_error); - CHECK_THROWS_WITH(j.push_back(k), "cannot use push_back() with number"); - } - } - } - - SECTION("to object") - { - SECTION("null") - { - json j; - j.push_back(json::object_t::value_type({"one", 1})); - j.push_back(json::object_t::value_type({"two", 2})); - CHECK(j.type() == json::value_t::object); - CHECK(j.size() == 2); - CHECK(j["one"] == json(1)); - CHECK(j["two"] == json(2)); - } - - SECTION("object") - { - json j(json::value_t::object); - j.push_back(json::object_t::value_type({"one", 1})); - j.push_back(json::object_t::value_type({"two", 2})); - CHECK(j.size() == 2); - CHECK(j["one"] == json(1)); - CHECK(j["two"] == json(2)); - } - - SECTION("other type") - { - json j = 1; - json k("Hello"); - CHECK_THROWS_AS(j.push_back(json::object_t::value_type({"one", 1})), std::domain_error); - CHECK_THROWS_WITH(j.push_back(json::object_t::value_type({"one", 1})), - "cannot use push_back() with number"); - } - } - - SECTION("with initializer_list") - { - SECTION("null") - { - json j; - j.push_back({"foo", "bar"}); - CHECK(j == json::array({{"foo", "bar"}})); - - json k; - k.push_back({1, 2, 3}); - CHECK(k == json::array({{1, 2, 3}})); - } - - SECTION("array") - { - json j = {1, 2, 3}; - j.push_back({"foo", "bar"}); - CHECK(j == json({1, 2, 3, {"foo", "bar"}})); - - json k = {1, 2, 3}; - k.push_back({1, 2, 3}); - CHECK(k == json({1, 2, 3, {1, 2, 3}})); - } - - SECTION("object") - { - json j = {{"key1", 1}}; - j.push_back({"key2", "bar"}); - CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); - - json k = {{"key1", 1}}; - CHECK_THROWS_AS(k.push_back({1, 2, 3, 4}), std::domain_error); - CHECK_THROWS_WITH(k.push_back({1, 2, 3, 4}), "cannot use push_back() with object"); - } - } - } - - SECTION("operator+=") - { - SECTION("to array") - { - SECTION("json&&") - { - SECTION("null") - { - json j; - j += 1; - j += 2; - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 2})); - } - - SECTION("array") - { - json j = {1, 2, 3}; - j += "Hello"; - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 2, 3, "Hello"})); - } - - SECTION("other type") - { - json j = 1; - CHECK_THROWS_AS(j += "Hello", std::domain_error); - CHECK_THROWS_WITH(j += "Hello", "cannot use push_back() with number"); - } - } - - SECTION("const json&") - { - SECTION("null") - { - json j; - json k(1); - j += k; - j += k; - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 1})); - } - - SECTION("array") - { - json j = {1, 2, 3}; - json k("Hello"); - j += k; - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 2, 3, "Hello"})); - } - - SECTION("other type") - { - json j = 1; - json k("Hello"); - CHECK_THROWS_AS(j += k, std::domain_error); - CHECK_THROWS_WITH(j += k, "cannot use push_back() with number"); - } - } - } - - SECTION("to object") - { - SECTION("null") - { - json j; - j += json::object_t::value_type({"one", 1}); - j += json::object_t::value_type({"two", 2}); - CHECK(j.type() == json::value_t::object); - CHECK(j.size() == 2); - CHECK(j["one"] == json(1)); - CHECK(j["two"] == json(2)); - } - - SECTION("object") - { - json j(json::value_t::object); - j += json::object_t::value_type({"one", 1}); - j += json::object_t::value_type({"two", 2}); - CHECK(j.size() == 2); - CHECK(j["one"] == json(1)); - CHECK(j["two"] == json(2)); - } - - SECTION("other type") - { - json j = 1; - json k("Hello"); - CHECK_THROWS_AS(j += json::object_t::value_type({"one", 1}), std::domain_error); - CHECK_THROWS_WITH(j += json::object_t::value_type({"one", 1}), - "cannot use push_back() with number"); - } - } - - SECTION("with initializer_list") - { - SECTION("null") - { - json j; - j += {"foo", "bar"}; - CHECK(j == json::array({{"foo", "bar"}})); - - json k; - k += {1, 2, 3}; - CHECK(k == json::array({{1, 2, 3}})); - } - - SECTION("array") - { - json j = {1, 2, 3}; - j += {"foo", "bar"}; - CHECK(j == json({1, 2, 3, {"foo", "bar"}})); - - json k = {1, 2, 3}; - k += {1, 2, 3}; - CHECK(k == json({1, 2, 3, {1, 2, 3}})); - } - - SECTION("object") - { - json j = {{"key1", 1}}; - j += {"key2", "bar"}; - CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); - - json k = {{"key1", 1}}; - CHECK_THROWS_AS((k += {1, 2, 3, 4}), std::domain_error); - CHECK_THROWS_WITH((k += {1, 2, 3, 4}), "cannot use push_back() with object"); - } - } - } - - SECTION("insert") - { - json j_array = {1, 2, 3, 4}; - json j_value = 5; - - SECTION("value at position") - { - SECTION("insert before begin()") - { - auto it = j_array.insert(j_array.begin(), j_value); - CHECK(j_array.size() == 5); - CHECK(*it == j_value); - CHECK(j_array.begin() == it); - CHECK(j_array == json({5, 1, 2, 3, 4})); - } - - SECTION("insert in the middle") - { - auto it = j_array.insert(j_array.begin() + 2, j_value); - CHECK(j_array.size() == 5); - CHECK(*it == j_value); - CHECK((it - j_array.begin()) == 2); - CHECK(j_array == json({1, 2, 5, 3, 4})); - } - - SECTION("insert before end()") - { - auto it = j_array.insert(j_array.end(), j_value); - CHECK(j_array.size() == 5); - CHECK(*it == j_value); - CHECK((j_array.end() - it) == 1); - CHECK(j_array == json({1, 2, 3, 4, 5})); - } - } - - SECTION("rvalue at position") - { - SECTION("insert before begin()") - { - auto it = j_array.insert(j_array.begin(), 5); - CHECK(j_array.size() == 5); - CHECK(*it == j_value); - CHECK(j_array.begin() == it); - CHECK(j_array == json({5, 1, 2, 3, 4})); - } - - SECTION("insert in the middle") - { - auto it = j_array.insert(j_array.begin() + 2, 5); - CHECK(j_array.size() == 5); - CHECK(*it == j_value); - CHECK((it - j_array.begin()) == 2); - CHECK(j_array == json({1, 2, 5, 3, 4})); - } - - SECTION("insert before end()") - { - auto it = j_array.insert(j_array.end(), 5); - CHECK(j_array.size() == 5); - CHECK(*it == j_value); - CHECK((j_array.end() - it) == 1); - CHECK(j_array == json({1, 2, 3, 4, 5})); - } - } - - SECTION("copies at position") - { - SECTION("insert before begin()") - { - auto it = j_array.insert(j_array.begin(), 3, 5); - CHECK(j_array.size() == 7); - CHECK(*it == j_value); - CHECK(j_array.begin() == it); - CHECK(j_array == json({5, 5, 5, 1, 2, 3, 4})); - } - - SECTION("insert in the middle") - { - auto it = j_array.insert(j_array.begin() + 2, 3, 5); - CHECK(j_array.size() == 7); - CHECK(*it == j_value); - CHECK((it - j_array.begin()) == 2); - CHECK(j_array == json({1, 2, 5, 5, 5, 3, 4})); - } - - SECTION("insert before end()") - { - auto it = j_array.insert(j_array.end(), 3, 5); - CHECK(j_array.size() == 7); - CHECK(*it == j_value); - CHECK((j_array.end() - it) == 3); - CHECK(j_array == json({1, 2, 3, 4, 5, 5, 5})); - } - - SECTION("insert nothing (count = 0)") - { - auto pos = j_array.end(); - auto it = j_array.insert(j_array.end(), 0, 5); - CHECK(j_array.size() == 4); - CHECK(it == pos); - CHECK(j_array == json({1, 2, 3, 4})); - } - } - - SECTION("range") - { - json j_other_array = {"first", "second"}; - - SECTION("proper usage") - { - auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.end()); - CHECK(j_array.size() == 6); - CHECK(*it == *j_other_array.begin()); - CHECK((j_array.end() - it) == 2); - CHECK(j_array == json({1, 2, 3, 4, "first", "second"})); - } - - SECTION("empty range") - { - auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.begin()); - CHECK(j_array.size() == 4); - CHECK(it == j_array.end()); - CHECK(j_array == json({1, 2, 3, 4})); - } - - SECTION("invalid iterators") - { - json j_other_array2 = {"first", "second"}; - - CHECK_THROWS_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), std::domain_error); - CHECK_THROWS_AS(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), - std::domain_error); - - CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), - "passed iterators may not belong to container"); - CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), - "iterators do not fit"); - } - } - - SECTION("initializer list at position") - { - SECTION("insert before begin()") - { - auto it = j_array.insert(j_array.begin(), {7, 8, 9}); - CHECK(j_array.size() == 7); - CHECK(*it == json(7)); - CHECK(j_array.begin() == it); - CHECK(j_array == json({7, 8, 9, 1, 2, 3, 4})); - } - - SECTION("insert in the middle") - { - auto it = j_array.insert(j_array.begin() + 2, {7, 8, 9}); - CHECK(j_array.size() == 7); - CHECK(*it == json(7)); - CHECK((it - j_array.begin()) == 2); - CHECK(j_array == json({1, 2, 7, 8, 9, 3, 4})); - } - - SECTION("insert before end()") - { - auto it = j_array.insert(j_array.end(), {7, 8, 9}); - CHECK(j_array.size() == 7); - CHECK(*it == json(7)); - CHECK((j_array.end() - it) == 3); - CHECK(j_array == json({1, 2, 3, 4, 7, 8, 9})); - } - } - - SECTION("invalid iterator") - { - // pass iterator to a different array - json j_another_array = {1, 2}; - json j_yet_another_array = {"first", "second"}; - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10), std::domain_error); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_value), std::domain_error); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10, 11), std::domain_error); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), - j_yet_another_array.end()), std::domain_error); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), std::domain_error); - - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10), "iterator does not fit current value"); - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_value), - "iterator does not fit current value"); - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10, 11), - "iterator does not fit current value"); - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), - j_yet_another_array.end()), "iterator does not fit current value"); - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), - "iterator does not fit current value"); - } - - SECTION("non-array type") - { - // call insert on a non-array type - json j_nonarray = 3; - json j_yet_another_array = {"first", "second"}; - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10), std::domain_error); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_value), std::domain_error); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), std::domain_error); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), - j_yet_another_array.end()), std::domain_error); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10), "cannot use insert() with number"); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_value), "cannot use insert() with number"); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10, 11), "cannot use insert() with number"); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), - j_yet_another_array.end()), "cannot use insert() with number"); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), - "cannot use insert() with number"); - } - } - - SECTION("swap()") - { - SECTION("json") - { - SECTION("member swap") - { - json j("hello world"); - json k(42.23); - - j.swap(k); - - CHECK(j == json(42.23)); - CHECK(k == json("hello world")); - } - - SECTION("nonmember swap") - { - json j("hello world"); - json k(42.23); - - std::swap(j, k); - - CHECK(j == json(42.23)); - CHECK(k == json("hello world")); - } - } - - SECTION("array_t") - { - SECTION("array_t type") - { - json j = {1, 2, 3, 4}; - json::array_t a = {"foo", "bar", "baz"}; - - j.swap(a); - - CHECK(j == json({"foo", "bar", "baz"})); - - j.swap(a); - - CHECK(j == json({1, 2, 3, 4})); - } - - SECTION("non-array_t type") - { - json j = 17; - json::array_t a = {"foo", "bar", "baz"}; - - CHECK_THROWS_AS(j.swap(a), std::domain_error); - CHECK_THROWS_WITH(j.swap(a), "cannot use swap() with number"); - } - } - - SECTION("object_t") - { - SECTION("object_t type") - { - json j = {{"one", 1}, {"two", 2}}; - json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}}; - - j.swap(o); - - CHECK(j == json({{"cow", "Kuh"}, {"chicken", "Huhn"}})); - - j.swap(o); - - CHECK(j == json({{"one", 1}, {"two", 2}})); - } - - SECTION("non-object_t type") - { - json j = 17; - json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}}; - - CHECK_THROWS_AS(j.swap(o), std::domain_error); - CHECK_THROWS_WITH(j.swap(o), "cannot use swap() with number"); - } - } - - SECTION("string_t") - { - SECTION("string_t type") - { - json j = "Hello world"; - json::string_t s = "Hallo Welt"; - - j.swap(s); - - CHECK(j == json("Hallo Welt")); - - j.swap(s); - - CHECK(j == json("Hello world")); - } - - SECTION("non-string_t type") - { - json j = 17; - json::string_t s = "Hallo Welt"; - - CHECK_THROWS_AS(j.swap(s), std::domain_error); - CHECK_THROWS_WITH(j.swap(s), "cannot use swap() with number"); - } - } - } -} - -TEST_CASE("lexicographical comparison operators") -{ - SECTION("types") - { - std::vector j_types = - { - json::value_t::null, - json::value_t::boolean, - json::value_t::number_integer, - json::value_t::number_unsigned, - json::value_t::number_float, - json::value_t::object, - json::value_t::array, - json::value_t::string - }; - - SECTION("comparison: less") - { - std::vector> expected = - { - {false, true, true, true, true, true, true, true}, - {false, false, true, true, true, true, true, true}, - {false, false, false, false, false, true, true, true}, - {false, false, false, false, false, true, true, true}, - {false, false, false, false, false, true, true, true}, - {false, false, false, false, false, false, true, true}, - {false, false, false, false, false, false, false, true}, - {false, false, false, false, false, false, false, false} - }; - - for (size_t i = 0; i < j_types.size(); ++i) - { - for (size_t j = 0; j < j_types.size(); ++j) - { - CAPTURE(i); - CAPTURE(j); - // check precomputed values - CHECK(operator<(j_types[i], j_types[j]) == expected[i][j]); - } - } - } - } - - SECTION("values") - { - json j_values = - { - nullptr, nullptr, - 17, 42, - 8u, 13u, - 3.14159, 23.42, - "foo", "bar", - true, false, - {1, 2, 3}, {"one", "two", "three"}, - {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}} - }; - - SECTION("comparison: equal") - { - std::vector> expected = - { - {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true} - }; - - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - CAPTURE(i); - CAPTURE(j); - // check precomputed values - CHECK( (j_values[i] == j_values[j]) == expected[i][j] ); - } - } - - // comparison with discarded elements - json j_discarded(json::value_t::discarded); - for (size_t i = 0; i < j_values.size(); ++i) - { - CHECK( (j_values[i] == j_discarded) == false); - CHECK( (j_discarded == j_values[i]) == false); - CHECK( (j_discarded == j_discarded) == false); - } - - // compare with null pointer - json j_null; - CHECK(j_null == nullptr); - CHECK(nullptr == j_null); - } - - SECTION("comparison: not equal") - { - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - CAPTURE(i); - CAPTURE(j); - // check definition - CHECK( (j_values[i] != j_values[j]) == not(j_values[i] == j_values[j]) ); - } - } - - // compare with null pointer - json j_null; - CHECK( (j_null != nullptr) == false); - CHECK( (nullptr != j_null) == false); - CHECK( (j_null != nullptr) == not(j_null == nullptr)); - CHECK( (nullptr != j_null) == not(nullptr == j_null)); - } - - SECTION("comparison: less") - { - std::vector> expected = - { - {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, - {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, - {false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true}, - {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true}, - {false, false, true, true, false, true, false, true, true, true, false, false, true, true, true, true}, - {false, false, true, true, false, false, false, true, true, true, false, false, true, true, true, true}, - {false, false, true, true, true, true, false, true, true, true, false, false, true, true, true, true}, - {false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, - {false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true}, - {false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true}, - {false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false}, - {false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false}, - {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false} - }; - - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - CAPTURE(i); - CAPTURE(j); - // check precomputed values - CHECK( (j_values[i] < j_values[j]) == expected[i][j] ); - } - } - - // comparison with discarded elements - json j_discarded(json::value_t::discarded); - for (size_t i = 0; i < j_values.size(); ++i) - { - CAPTURE(i); - CHECK( (j_values[i] < j_discarded) == false); - CHECK( (j_discarded < j_values[i]) == false); - CHECK( (j_discarded < j_discarded) == false); - } - } - - SECTION("comparison: less than or equal equal") - { - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - CAPTURE(i); - CAPTURE(j); - // check definition - CHECK( (j_values[i] <= j_values[j]) == not(j_values[j] < j_values[i]) ); - } - } - } - - SECTION("comparison: greater than") - { - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - CAPTURE(i); - CAPTURE(j); - // check definition - CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) ); - } - } - } - - SECTION("comparison: greater than or equal") - { - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - CAPTURE(i); - CAPTURE(j); - // check definition - CHECK( (j_values[i] >= j_values[j]) == not(j_values[i] < j_values[j]) ); - } - } - } - } -} - -TEST_CASE("serialization") -{ - SECTION("operator<<") - { - SECTION("no given width") - { - std::stringstream ss; - json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; - ss << j; - CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]"); - } - - SECTION("given width") - { - std::stringstream ss; - json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; - ss << std::setw(4) << j; - CHECK(ss.str() == - "[\n \"foo\",\n 1,\n 2,\n 3,\n false,\n {\n \"one\": 1\n }\n]"); - } - } - - SECTION("operator>>") - { - SECTION("no given width") - { - std::stringstream ss; - json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; - j >> ss; - CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]"); - } - - SECTION("given width") - { - std::stringstream ss; - json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; - ss.width(4); - j >> ss; - CHECK(ss.str() == - "[\n \"foo\",\n 1,\n 2,\n 3,\n false,\n {\n \"one\": 1\n }\n]"); - } - } -} - -TEST_CASE("deserialization") -{ - SECTION("stream") - { - std::stringstream ss; - ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; - json j = json::parse(ss); - CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); - } - - SECTION("string") - { - auto s = "[\"foo\",1,2,3,false,{\"one\":1}]"; - json j = json::parse(s); - CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); - } - - SECTION("operator<<") - { - std::stringstream ss; - ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; - json j; - j << ss; - CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); - } - - SECTION("operator>>") - { - std::stringstream ss; - ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; - json j; - ss >> j; - CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); - } - - SECTION("user-defined string literal") - { - CHECK("[\"foo\",1,2,3,false,{\"one\":1}]"_json == json({"foo", 1, 2, 3, false, {{"one", 1}}})); - } -} - -TEST_CASE("iterator class") -{ - SECTION("construction") - { - SECTION("constructor") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it(&j); - } - - SECTION("object") - { - json j(json::value_t::object); - json::iterator it(&j); - } - - SECTION("array") - { - json j(json::value_t::array); - json::iterator it(&j); - } - } - - SECTION("copy assignment") - { - json j(json::value_t::null); - json::iterator it(&j); - json::iterator it2(&j); - it2 = it; - } - } - - SECTION("initialization") - { - SECTION("set_begin") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it(&j); - it.set_begin(); - CHECK(it == j.begin()); - } - - SECTION("object") - { - json j(json::value_t::object); - json::iterator it(&j); - it.set_begin(); - CHECK(it == j.begin()); - } - - SECTION("array") - { - json j(json::value_t::array); - json::iterator it(&j); - it.set_begin(); - CHECK(it == j.begin()); - } - } - - SECTION("set_end") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it(&j); - it.set_end(); - CHECK(it == j.end()); - } - - SECTION("object") - { - json j(json::value_t::object); - json::iterator it(&j); - it.set_end(); - CHECK(it == j.end()); - } - - SECTION("array") - { - json j(json::value_t::array); - json::iterator it(&j); - it.set_end(); - CHECK(it == j.end()); - } - } - } - - SECTION("element access") - { - SECTION("operator*") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it = j.begin(); - CHECK_THROWS_AS(*it, std::out_of_range); - CHECK_THROWS_WITH(*it, "cannot get value"); - } - - SECTION("number") - { - json j(17); - json::iterator it = j.begin(); - CHECK(*it == json(17)); - it = j.end(); - CHECK_THROWS_AS(*it, std::out_of_range); - CHECK_THROWS_WITH(*it, "cannot get value"); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::iterator it = j.begin(); - CHECK(*it == json("bar")); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::iterator it = j.begin(); - CHECK(*it == json(1)); - } - } - - SECTION("operator->") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it = j.begin(); - CHECK_THROWS_AS(it->type_name(), std::out_of_range); - CHECK_THROWS_WITH(it->type_name(), "cannot get value"); - } - - SECTION("number") - { - json j(17); - json::iterator it = j.begin(); - CHECK(it->type_name() == "number"); - it = j.end(); - CHECK_THROWS_AS(it->type_name(), std::out_of_range); - CHECK_THROWS_WITH(it->type_name(), "cannot get value"); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::iterator it = j.begin(); - CHECK(it->type_name() == "string"); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::iterator it = j.begin(); - CHECK(it->type_name() == "number"); - } - } - } - - SECTION("increment/decrement") - { - SECTION("post-increment") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it = j.begin(); - CHECK(it.m_it.primitive_iterator == 1); - it++; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("number") - { - json j(17); - json::iterator it = j.begin(); - CHECK(it.m_it.primitive_iterator == 0); - it++; - CHECK(it.m_it.primitive_iterator == 1); - it++; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::iterator it = j.begin(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - it++; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::iterator it = j.begin(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - } - } - - SECTION("pre-increment") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it = j.begin(); - CHECK(it.m_it.primitive_iterator == 1); - ++it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("number") - { - json j(17); - json::iterator it = j.begin(); - CHECK(it.m_it.primitive_iterator == 0); - ++it; - CHECK(it.m_it.primitive_iterator == 1); - ++it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::iterator it = j.begin(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - ++it; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::iterator it = j.begin(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - } - } - - SECTION("post-decrement") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it = j.end(); - CHECK(it.m_it.primitive_iterator == 1); - } - - SECTION("number") - { - json j(17); - json::iterator it = j.end(); - CHECK(it.m_it.primitive_iterator == 1); - it--; - CHECK(it.m_it.primitive_iterator == 0); - it--; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::iterator it = j.end(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - it--; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::iterator it = j.end(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - } - } - - SECTION("pre-decrement") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it = j.end(); - CHECK(it.m_it.primitive_iterator == 1); - } - - SECTION("number") - { - json j(17); - json::iterator it = j.end(); - CHECK(it.m_it.primitive_iterator == 1); - --it; - CHECK(it.m_it.primitive_iterator == 0); - --it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::iterator it = j.end(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - --it; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::iterator it = j.end(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - } - } - } -} - -TEST_CASE("const_iterator class") -{ - SECTION("construction") - { - SECTION("constructor") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it(&j); - } - - SECTION("object") - { - json j(json::value_t::object); - json::const_iterator it(&j); - } - - SECTION("array") - { - json j(json::value_t::array); - json::const_iterator it(&j); - } - } - - SECTION("copy assignment") - { - json j(json::value_t::null); - json::const_iterator it(&j); - json::const_iterator it2(&j); - it2 = it; - } - } - - SECTION("initialization") - { - SECTION("set_begin") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it(&j); - it.set_begin(); - CHECK(it == j.cbegin()); - } - - SECTION("object") - { - json j(json::value_t::object); - json::const_iterator it(&j); - it.set_begin(); - CHECK(it == j.cbegin()); - } - - SECTION("array") - { - json j(json::value_t::array); - json::const_iterator it(&j); - it.set_begin(); - CHECK(it == j.cbegin()); - } - } - - SECTION("set_end") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it(&j); - it.set_end(); - CHECK(it == j.cend()); - } - - SECTION("object") - { - json j(json::value_t::object); - json::const_iterator it(&j); - it.set_end(); - CHECK(it == j.cend()); - } - - SECTION("array") - { - json j(json::value_t::array); - json::const_iterator it(&j); - it.set_end(); - CHECK(it == j.cend()); - } - } - } - - SECTION("element access") - { - SECTION("operator*") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it = j.cbegin(); - CHECK_THROWS_AS(*it, std::out_of_range); - CHECK_THROWS_WITH(*it, "cannot get value"); - } - - SECTION("number") - { - json j(17); - json::const_iterator it = j.cbegin(); - CHECK(*it == json(17)); - it = j.cend(); - CHECK_THROWS_AS(*it, std::out_of_range); - CHECK_THROWS_WITH(*it, "cannot get value"); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::const_iterator it = j.cbegin(); - CHECK(*it == json("bar")); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::const_iterator it = j.cbegin(); - CHECK(*it == json(1)); - } - } - - SECTION("operator->") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it = j.cbegin(); - CHECK_THROWS_AS(it->type_name(), std::out_of_range); - CHECK_THROWS_WITH(it->type_name(), "cannot get value"); - } - - SECTION("number") - { - json j(17); - json::const_iterator it = j.cbegin(); - CHECK(it->type_name() == "number"); - it = j.cend(); - CHECK_THROWS_AS(it->type_name(), std::out_of_range); - CHECK_THROWS_WITH(it->type_name(), "cannot get value"); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::const_iterator it = j.cbegin(); - CHECK(it->type_name() == "string"); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::const_iterator it = j.cbegin(); - CHECK(it->type_name() == "number"); - } - } - } - - SECTION("increment/decrement") - { - SECTION("post-increment") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.primitive_iterator == 1); - it++; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("number") - { - json j(17); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.primitive_iterator == 0); - it++; - CHECK(it.m_it.primitive_iterator == 1); - it++; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - it++; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - } - } - - SECTION("pre-increment") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.primitive_iterator == 1); - ++it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("number") - { - json j(17); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.primitive_iterator == 0); - ++it; - CHECK(it.m_it.primitive_iterator == 1); - ++it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - ++it; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - } - } - - SECTION("post-decrement") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it = j.cend(); - CHECK(it.m_it.primitive_iterator == 1); - } - - SECTION("number") - { - json j(17); - json::const_iterator it = j.cend(); - CHECK(it.m_it.primitive_iterator == 1); - it--; - CHECK(it.m_it.primitive_iterator == 0); - it--; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::const_iterator it = j.cend(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - it--; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::const_iterator it = j.cend(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - } - } - - SECTION("pre-decrement") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it = j.cend(); - CHECK(it.m_it.primitive_iterator == 1); - } - - SECTION("number") - { - json j(17); - json::const_iterator it = j.cend(); - CHECK(it.m_it.primitive_iterator == 1); - --it; - CHECK(it.m_it.primitive_iterator == 0); - --it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::const_iterator it = j.cend(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - --it; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::const_iterator it = j.cend(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - } - } - } -} - -TEST_CASE("convenience functions") -{ - SECTION("type name as string") - { - CHECK(json(json::value_t::null).type_name() == "null"); - CHECK(json(json::value_t::object).type_name() == "object"); - CHECK(json(json::value_t::array).type_name() == "array"); - CHECK(json(json::value_t::number_integer).type_name() == "number"); - CHECK(json(json::value_t::number_float).type_name() == "number"); - CHECK(json(json::value_t::boolean).type_name() == "boolean"); - CHECK(json(json::value_t::string).type_name() == "string"); - CHECK(json(json::value_t::discarded).type_name() == "discarded"); - } - - SECTION("string escape") - { - CHECK(json::escape_string("\"") == "\\\""); - CHECK(json::escape_string("\\") == "\\\\"); - CHECK(json::escape_string("\b") == "\\b"); - CHECK(json::escape_string("\f") == "\\f"); - CHECK(json::escape_string("\n") == "\\n"); - CHECK(json::escape_string("\r") == "\\r"); - CHECK(json::escape_string("\t") == "\\t"); - - CHECK(json::escape_string("\x01") == "\\u0001"); - CHECK(json::escape_string("\x02") == "\\u0002"); - CHECK(json::escape_string("\x03") == "\\u0003"); - CHECK(json::escape_string("\x04") == "\\u0004"); - CHECK(json::escape_string("\x05") == "\\u0005"); - CHECK(json::escape_string("\x06") == "\\u0006"); - CHECK(json::escape_string("\x07") == "\\u0007"); - CHECK(json::escape_string("\x08") == "\\b"); - CHECK(json::escape_string("\x09") == "\\t"); - CHECK(json::escape_string("\x0a") == "\\n"); - CHECK(json::escape_string("\x0b") == "\\u000b"); - CHECK(json::escape_string("\x0c") == "\\f"); - CHECK(json::escape_string("\x0d") == "\\r"); - CHECK(json::escape_string("\x0e") == "\\u000e"); - CHECK(json::escape_string("\x0f") == "\\u000f"); - CHECK(json::escape_string("\x10") == "\\u0010"); - CHECK(json::escape_string("\x11") == "\\u0011"); - CHECK(json::escape_string("\x12") == "\\u0012"); - CHECK(json::escape_string("\x13") == "\\u0013"); - CHECK(json::escape_string("\x14") == "\\u0014"); - CHECK(json::escape_string("\x15") == "\\u0015"); - CHECK(json::escape_string("\x16") == "\\u0016"); - CHECK(json::escape_string("\x17") == "\\u0017"); - CHECK(json::escape_string("\x18") == "\\u0018"); - CHECK(json::escape_string("\x19") == "\\u0019"); - CHECK(json::escape_string("\x1a") == "\\u001a"); - CHECK(json::escape_string("\x1b") == "\\u001b"); - CHECK(json::escape_string("\x1c") == "\\u001c"); - CHECK(json::escape_string("\x1d") == "\\u001d"); - CHECK(json::escape_string("\x1e") == "\\u001e"); - CHECK(json::escape_string("\x1f") == "\\u001f"); - } -} - -TEST_CASE("lexer class") -{ - SECTION("scan") - { - SECTION("structural characters") - { - CHECK(json::lexer("[").scan() == json::lexer::token_type::begin_array); - CHECK(json::lexer("]").scan() == json::lexer::token_type::end_array); - CHECK(json::lexer("{").scan() == json::lexer::token_type::begin_object); - CHECK(json::lexer("}").scan() == json::lexer::token_type::end_object); - CHECK(json::lexer(",").scan() == json::lexer::token_type::value_separator); - CHECK(json::lexer(":").scan() == json::lexer::token_type::name_separator); - } - - SECTION("literal names") - { - CHECK(json::lexer("null").scan() == json::lexer::token_type::literal_null); - CHECK(json::lexer("true").scan() == json::lexer::token_type::literal_true); - CHECK(json::lexer("false").scan() == json::lexer::token_type::literal_false); - } - - SECTION("numbers") - { - CHECK(json::lexer("0").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("1").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("2").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("3").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("4").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("5").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("6").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("7").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("8").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("9").scan() == json::lexer::token_type::value_number); - } - - SECTION("whitespace") - { - // result is end_of_input, because not token is following - CHECK(json::lexer(" ").scan() == json::lexer::token_type::end_of_input); - CHECK(json::lexer("\t").scan() == json::lexer::token_type::end_of_input); - CHECK(json::lexer("\n").scan() == json::lexer::token_type::end_of_input); - CHECK(json::lexer("\r").scan() == json::lexer::token_type::end_of_input); - CHECK(json::lexer(" \t\n\r\n\t ").scan() == json::lexer::token_type::end_of_input); - } - } - - SECTION("token_type_name") - { - CHECK(json::lexer::token_type_name(json::lexer::token_type::uninitialized) == ""); - CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_true) == "true literal"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_false) == "false literal"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_null) == "null literal"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::value_string) == "string literal"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::value_number) == "number literal"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_array) == "'['"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_object) == "'{'"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::end_array) == "']'"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::end_object) == "'}'"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::name_separator) == "':'"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::value_separator) == "','"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::parse_error) == ""); - CHECK(json::lexer::token_type_name(json::lexer::token_type::end_of_input) == "end of input"); - } - - SECTION("parse errors on first character") - { - for (int c = 1; c < 128; ++c) - { - auto s = std::string(1, c); - - switch (c) - { - // single characters that are valid tokens - case ('['): - case (']'): - case ('{'): - case ('}'): - case (','): - case (':'): - case ('0'): - case ('1'): - case ('2'): - case ('3'): - case ('4'): - case ('5'): - case ('6'): - case ('7'): - case ('8'): - case ('9'): - { - CHECK(json::lexer(s.c_str()).scan() != json::lexer::token_type::parse_error); - break; - } - - // whitespace - case (' '): - case ('\t'): - case ('\n'): - case ('\r'): - { - CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::end_of_input); - break; - } - - // anything else is not expected - default: - { - CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::parse_error); - break; - } - } - } - } - - SECTION("to_unicode") - { - CHECK(json::lexer::to_unicode(0x1F4A9) == "💩"); - CHECK_THROWS_AS(json::lexer::to_unicode(0x200000), std::out_of_range); - CHECK_THROWS_WITH(json::lexer::to_unicode(0x200000), "code points above 0x10FFFF are invalid"); - } -} - -TEST_CASE("parser class") -{ - SECTION("parse") - { - SECTION("null") - { - CHECK(json::parser("null").parse() == json(nullptr)); - } - - SECTION("true") - { - CHECK(json::parser("true").parse() == json(true)); - } - - SECTION("false") - { - CHECK(json::parser("false").parse() == json(false)); - } - - SECTION("array") - { - SECTION("empty array") - { - CHECK(json::parser("[]").parse() == json(json::value_t::array)); - CHECK(json::parser("[ ]").parse() == json(json::value_t::array)); - } - - SECTION("nonempty array") - { - CHECK(json::parser("[true, false, null]").parse() == json({true, false, nullptr})); - } - } - - SECTION("object") - { - SECTION("empty object") - { - CHECK(json::parser("{}").parse() == json(json::value_t::object)); - CHECK(json::parser("{ }").parse() == json(json::value_t::object)); - } - - SECTION("nonempty object") - { - CHECK(json::parser("{\"\": true, \"one\": 1, \"two\": null}").parse() == json({{"", true}, {"one", 1}, {"two", nullptr}})); - } - } - - SECTION("string") - { - // empty string - CHECK(json::parser("\"\"").parse() == json(json::value_t::string)); - - SECTION("errors") - { - // error: tab in string - CHECK_THROWS_AS(json::parser("\"\t\"").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("\"\t\"").parse(), "parse error - unexpected '\"'"); - // error: newline in string - CHECK_THROWS_AS(json::parser("\"\n\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\r\"").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("\"\n\"").parse(), "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser("\"\r\"").parse(), "parse error - unexpected '\"'"); - // error: backspace in string - CHECK_THROWS_AS(json::parser("\"\b\"").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("\"\b\"").parse(), "parse error - unexpected '\"'"); - // improve code coverage - CHECK_THROWS_AS(json::parser("\uFF01").parse(), std::invalid_argument); - // unescaped control characters - CHECK_THROWS_AS(json::parser("\"\x00\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x01\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x02\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x03\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x04\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x05\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x06\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x07\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x08\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x09\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0a\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0b\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0c\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0d\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0e\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0f\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x10\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x11\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x12\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x13\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x14\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x15\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x16\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x17\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x18\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x19\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1a\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1b\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1c\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1d\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1e\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1f\"").parse(), std::invalid_argument); - } - - SECTION("escaped") - { - // quotation mark "\"" - auto r1 = R"("\"")"_json; - CHECK(json::parser("\"\\\"\"").parse() == r1); - // reverse solidus "\\" - auto r2 = R"("\\")"_json; - CHECK(json::parser("\"\\\\\"").parse() == r2); - // solidus - CHECK(json::parser("\"\\/\"").parse() == R"("/")"_json); - // backspace - CHECK(json::parser("\"\\b\"").parse() == json("\b")); - // formfeed - CHECK(json::parser("\"\\f\"").parse() == json("\f")); - // newline - CHECK(json::parser("\"\\n\"").parse() == json("\n")); - // carriage return - CHECK(json::parser("\"\\r\"").parse() == json("\r")); - // horizontal tab - CHECK(json::parser("\"\\t\"").parse() == json("\t")); - - CHECK(json::parser("\"\\u0001\"").parse().get() == "\x01"); - CHECK(json::parser("\"\\u000a\"").parse().get() == "\n"); - CHECK(json::parser("\"\\u00b0\"").parse().get() == "°"); - CHECK(json::parser("\"\\u0c00\"").parse().get() == "ఀ"); - CHECK(json::parser("\"\\ud000\"").parse().get() == "퀀"); - CHECK(json::parser("\"\\u000E\"").parse().get() == "\x0E"); - CHECK(json::parser("\"\\u00F0\"").parse().get() == "ð"); - CHECK(json::parser("\"\\u0100\"").parse().get() == "Ā"); - CHECK(json::parser("\"\\u2000\"").parse().get() == " "); - CHECK(json::parser("\"\\uFFFF\"").parse().get() == "￿"); - CHECK(json::parser("\"\\u20AC\"").parse().get() == "€"); - CHECK(json::parser("\"€\"").parse().get() == "€"); - CHECK(json::parser("\"🎈\"").parse().get() == "🎈"); - - CHECK(json::parse("\"\\ud80c\\udc60\"").get() == u8"\U00013060"); - CHECK(json::parse("\"\\ud83c\\udf1e\"").get() == "🌞"); - } - } - - SECTION("number") - { - SECTION("integers") - { - SECTION("without exponent") - { - CHECK(json::parser("-128").parse() == json(-128)); - CHECK(json::parser("-0").parse() == json(-0)); - CHECK(json::parser("0").parse() == json(0)); - CHECK(json::parser("128").parse() == json(128)); - } - - SECTION("with exponent") - { - CHECK(json::parser("0e1").parse() == json(0e1)); - CHECK(json::parser("0E1").parse() == json(0e1)); - - CHECK(json::parser("10000E-4").parse() == json(10000e-4)); - CHECK(json::parser("10000E-3").parse() == json(10000e-3)); - CHECK(json::parser("10000E-2").parse() == json(10000e-2)); - CHECK(json::parser("10000E-1").parse() == json(10000e-1)); - CHECK(json::parser("10000E0").parse() == json(10000e0)); - CHECK(json::parser("10000E1").parse() == json(10000e1)); - CHECK(json::parser("10000E2").parse() == json(10000e2)); - CHECK(json::parser("10000E3").parse() == json(10000e3)); - CHECK(json::parser("10000E4").parse() == json(10000e4)); - - CHECK(json::parser("10000e-4").parse() == json(10000e-4)); - CHECK(json::parser("10000e-3").parse() == json(10000e-3)); - CHECK(json::parser("10000e-2").parse() == json(10000e-2)); - CHECK(json::parser("10000e-1").parse() == json(10000e-1)); - CHECK(json::parser("10000e0").parse() == json(10000e0)); - CHECK(json::parser("10000e1").parse() == json(10000e1)); - CHECK(json::parser("10000e2").parse() == json(10000e2)); - CHECK(json::parser("10000e3").parse() == json(10000e3)); - CHECK(json::parser("10000e4").parse() == json(10000e4)); - - CHECK(json::parser("-0e1").parse() == json(-0e1)); - CHECK(json::parser("-0E1").parse() == json(-0e1)); - CHECK(json::parser("-0E123").parse() == json(-0e123)); - } - - SECTION("edge cases") - { - // From RFC7159, Section 6: - // Note that when such software is used, numbers that are - // integers and are in the range [-(2**53)+1, (2**53)-1] - // are interoperable in the sense that implementations will - // agree exactly on their numeric values. - - // -(2**53)+1 - CHECK(json::parser("-9007199254740991").parse().get() == -9007199254740991); - // (2**53)-1 - CHECK(json::parser("9007199254740991").parse().get() == 9007199254740991); - } - - SECTION("over the edge cases") // issue #178 - Integer conversion to unsigned (incorrect handling of 64 bit integers) - { - // While RFC7159, Section 6 specifies a preference for support - // for ranges in range of IEEE 754-2008 binary64 (double precision) - // this does not accommodate 64 bit integers without loss of accuracy. - // As 64 bit integers are now widely used in software, it is desirable - // to expand support to to the full 64 bit (signed and unsigned) range - // i.e. -(2**63) -> (2**64)-1. - - // -(2**63) ** Note: compilers see negative literals as negated positive numbers (hence the -1)) - CHECK(json::parser("-9223372036854775808").parse().get() == -9223372036854775807 - 1); - // (2**63)-1 - CHECK(json::parser("9223372036854775807").parse().get() == 9223372036854775807); - // (2**64)-1 - CHECK(json::parser("18446744073709551615").parse().get() == 18446744073709551615u); - } - } - - SECTION("floating-point") - { - SECTION("without exponent") - { - CHECK(json::parser("-128.5").parse() == json(-128.5)); - CHECK(json::parser("0.999").parse() == json(0.999)); - CHECK(json::parser("128.5").parse() == json(128.5)); - CHECK(json::parser("-0.0").parse() == json(-0.0)); - } - - SECTION("with exponent") - { - CHECK(json::parser("-128.5E3").parse() == json(-128.5E3)); - CHECK(json::parser("-128.5E-3").parse() == json(-128.5E-3)); - CHECK(json::parser("-0.0e1").parse() == json(-0.0e1)); - CHECK(json::parser("-0.0E1").parse() == json(-0.0e1)); - } - } - - SECTION("invalid numbers") - { - CHECK_THROWS_AS(json::parser("01").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("--1").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E-").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1.E1").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-1E").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0E#").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0E-#").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0#").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0.0:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0.0Z").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0E123:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0e0-:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0e-:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0f").parse(), std::invalid_argument); - - // numbers must not begin with "+" - CHECK_THROWS_AS(json::parser("+1").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("+0").parse(), std::invalid_argument); - - CHECK_THROWS_WITH(json::parser("01").parse(), - "parse error - unexpected number literal; expected end of input"); - CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'"); - CHECK_THROWS_WITH(json::parser("1.").parse(), - "parse error - unexpected '.'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1E").parse(), - "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1E-").parse(), - "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1.E1").parse(), - "parse error - unexpected '.'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-1E").parse(), - "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0E#").parse(), - "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0E-#").parse(), - "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0#").parse(), - "parse error - unexpected '#'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0.0:").parse(), - "parse error - unexpected ':'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0.0Z").parse(), - "parse error - unexpected 'Z'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0E123:").parse(), - "parse error - unexpected ':'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0e0-:").parse(), - "parse error - unexpected '-'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0e-:").parse(), - "parse error - unexpected 'e'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0f").parse(), - "parse error - unexpected 'f'; expected end of input"); - } - } - } - - SECTION("parse errors") - { - // unexpected end of number - CHECK_THROWS_AS(json::parser("0.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("--").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("0.:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("e.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1e.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1e/").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1e:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E/").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E:").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("0.").parse(), - "parse error - unexpected '.'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-").parse(), "parse error - unexpected '-'"); - CHECK_THROWS_WITH(json::parser("--").parse(), - "parse error - unexpected '-'"); - CHECK_THROWS_WITH(json::parser("-0.").parse(), - "parse error - unexpected '.'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-.").parse(), - "parse error - unexpected '-'"); - CHECK_THROWS_WITH(json::parser("-:").parse(), - "parse error - unexpected '-'"); - CHECK_THROWS_WITH(json::parser("0.:").parse(), - "parse error - unexpected '.'; expected end of input"); - CHECK_THROWS_WITH(json::parser("e.").parse(), - "parse error - unexpected 'e'"); - CHECK_THROWS_WITH(json::parser("1e.").parse(), - "parse error - unexpected 'e'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1e/").parse(), - "parse error - unexpected 'e'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1e:").parse(), - "parse error - unexpected 'e'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1E.").parse(), - "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1E/").parse(), - "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1E:").parse(), - "parse error - unexpected 'E'; expected end of input"); - - // unexpected end of null - CHECK_THROWS_AS(json::parser("n").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("nu").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("nul").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("n").parse(), "parse error - unexpected 'n'"); - CHECK_THROWS_WITH(json::parser("nu").parse(), - "parse error - unexpected 'n'"); - CHECK_THROWS_WITH(json::parser("nul").parse(), - "parse error - unexpected 'n'"); - - // unexpected end of true - CHECK_THROWS_AS(json::parser("t").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("tr").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("tru").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("t").parse(), "parse error - unexpected 't'"); - CHECK_THROWS_WITH(json::parser("tr").parse(), - "parse error - unexpected 't'"); - CHECK_THROWS_WITH(json::parser("tru").parse(), - "parse error - unexpected 't'"); - - // unexpected end of false - CHECK_THROWS_AS(json::parser("f").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("fa").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("fal").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("fals").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("f").parse(), "parse error - unexpected 'f'"); - CHECK_THROWS_WITH(json::parser("fa").parse(), - "parse error - unexpected 'f'"); - CHECK_THROWS_WITH(json::parser("fal").parse(), - "parse error - unexpected 'f'"); - CHECK_THROWS_WITH(json::parser("fals").parse(), - "parse error - unexpected 'f'"); - - // missing/unexpected end of array - CHECK_THROWS_AS(json::parser("[").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("[1").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("[1,").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("[1,]").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("]").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("[").parse(), - "parse error - unexpected end of input"); - CHECK_THROWS_WITH(json::parser("[1").parse(), - "parse error - unexpected end of input; expected ']'"); - CHECK_THROWS_WITH(json::parser("[1,").parse(), - "parse error - unexpected end of input"); - CHECK_THROWS_WITH(json::parser("[1,]").parse(), - "parse error - unexpected ']'"); - CHECK_THROWS_WITH(json::parser("]").parse(), "parse error - unexpected ']'"); - - // missing/unexpected end of object - CHECK_THROWS_AS(json::parser("{").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("{\"foo\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("{\"foo\":").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("{\"foo\":}").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("{\"foo\":1,}").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("}").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("{").parse(), - "parse error - unexpected end of input; expected string literal"); - CHECK_THROWS_WITH(json::parser("{\"foo\"").parse(), - "parse error - unexpected end of input; expected ':'"); - CHECK_THROWS_WITH(json::parser("{\"foo\":").parse(), - "parse error - unexpected end of input"); - CHECK_THROWS_WITH(json::parser("{\"foo\":}").parse(), - "parse error - unexpected '}'"); - CHECK_THROWS_WITH(json::parser("{\"foo\":1,}").parse(), - "parse error - unexpected '}'; expected string literal"); - CHECK_THROWS_WITH(json::parser("}").parse(), "parse error - unexpected '}'"); - - // missing/unexpected end of string - CHECK_THROWS_AS(json::parser("\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u0\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u01\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u012\"").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("\"").parse(), - "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser("\"\\\"").parse(), - "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser("\"\\u\"").parse(), - "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser("\"\\u0\"").parse(), - "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser("\"\\u01\"").parse(), - "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser("\"\\u012\"").parse(), - "parse error - unexpected '\"'"); - - // invalid escapes - for (int c = 1; c < 128; ++c) - { - auto s = std::string("\"\\") + std::string(1, c) + "\""; - - switch (c) - { - // valid escapes - case ('"'): - case ('\\'): - case ('/'): - case ('b'): - case ('f'): - case ('n'): - case ('r'): - case ('t'): - { - CHECK_NOTHROW(json::parser(s).parse()); - break; - } - - // \u must be followed with four numbers, so we skip it here - case ('u'): - { - break; - } - - // any other combination of backslash and character is invalid - default: - { - CHECK_THROWS_AS(json::parser(s).parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser(s).parse(), "parse error - unexpected '\"'"); - break; - } - } - } - - // invalid \uxxxx escapes - { - // check whether character is a valid hex character - const auto valid = [](int c) - { - switch (c) - { - case ('0'): - case ('1'): - case ('2'): - case ('3'): - case ('4'): - case ('5'): - case ('6'): - case ('7'): - case ('8'): - case ('9'): - case ('a'): - case ('b'): - case ('c'): - case ('d'): - case ('e'): - case ('f'): - case ('A'): - case ('B'): - case ('C'): - case ('D'): - case ('E'): - case ('F'): - { - return true; - } - - default: - { - return false; - } - } - }; - - for (int c = 1; c < 128; ++c) - { - std::string s = "\"\\u"; - - // create a string with the iterated character at each position - auto s1 = s + "000" + std::string(1, c) + "\""; - auto s2 = s + "00" + std::string(1, c) + "0\""; - auto s3 = s + "0" + std::string(1, c) + "00\""; - auto s4 = s + std::string(1, c) + "000\""; - - if (valid(c)) - { - CHECK_NOTHROW(json::parser(s1).parse()); - CHECK_NOTHROW(json::parser(s2).parse()); - CHECK_NOTHROW(json::parser(s3).parse()); - CHECK_NOTHROW(json::parser(s4).parse()); - } - else - { - CHECK_THROWS_AS(json::parser(s1).parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser(s2).parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser(s3).parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser(s4).parse(), std::invalid_argument); - - CHECK_THROWS_WITH(json::parser(s1).parse(), "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser(s2).parse(), "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser(s3).parse(), "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser(s4).parse(), "parse error - unexpected '\"'"); - } - } - } - - // missing part of a surrogate pair - CHECK_THROWS_AS(json::parse("\"\\uD80C\""), std::invalid_argument); - CHECK_THROWS_WITH(json::parse("\"\\uD80C\""), "missing low surrogate"); - // invalid surrogate pair - CHECK_THROWS_AS(json::parse("\"\\uD80C\\uD80C\""), std::invalid_argument); - CHECK_THROWS_AS(json::parse("\"\\uD80C\\u0000\""), std::invalid_argument); - CHECK_THROWS_AS(json::parse("\"\\uD80C\\uFFFF\""), std::invalid_argument); - CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uD80C\""), - "missing or wrong low surrogate"); - CHECK_THROWS_WITH(json::parse("\"\\uD80C\\u0000\""), - "missing or wrong low surrogate"); - CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uFFFF\""), - "missing or wrong low surrogate"); - } - - SECTION("callback function") - { - auto s_object = R"( - { - "foo": 2, - "bar": { - "baz": 1 - } - } - )"; - - auto s_array = R"( - [1,2,[3,4,5],4,5] - )"; - - SECTION("filter nothing") - { - json j_object = json::parse(s_object, [](int, json::parse_event_t, const json&) - { - return true; - }); - - CHECK (j_object == json({{"foo", 2}, {"bar", {{"baz", 1}}}})); - - json j_array = json::parse(s_array, [](int, json::parse_event_t, const json&) - { - return true; - }); - - CHECK (j_array == json({1, 2, {3, 4, 5}, 4, 5})); - } - - SECTION("filter everything") - { - json j_object = json::parse(s_object, [](int, json::parse_event_t, const json&) - { - return false; - }); - - // the top-level object will be discarded, leaving a null - CHECK (j_object.is_null()); - - json j_array = json::parse(s_array, [](int, json::parse_event_t, const json&) - { - return false; - }); - - // the top-level array will be discarded, leaving a null - CHECK (j_array.is_null()); - } - - SECTION("filter specific element") - { - json j_object = json::parse(s_object, [](int, json::parse_event_t, const json & j) - { - // filter all number(2) elements - if (j == json(2)) - { - return false; - } - else - { - return true; - } - }); - - CHECK (j_object == json({{"bar", {{"baz", 1}}}})); - - json j_array = json::parse(s_array, [](int, json::parse_event_t, const json & j) - { - if (j == json(2)) - { - return false; - } - else - { - return true; - } - }); - - CHECK (j_array == json({1, {3, 4, 5}, 4, 5})); - } - - SECTION("filter specific events") - { - SECTION("first closing event") - { - { - json j_object = json::parse(s_object, [](int, json::parse_event_t e, const json&) - { - static bool first = true; - if (e == json::parse_event_t::object_end and first) - { - first = false; - return false; - } - else - { - return true; - } - }); - - // the first completed object will be discarded - CHECK (j_object == json({{"foo", 2}})); - } - - { - json j_array = json::parse(s_array, [](int, json::parse_event_t e, const json&) - { - static bool first = true; - if (e == json::parse_event_t::array_end and first) - { - first = false; - return false; - } - else - { - return true; - } - }); - - // the first completed array will be discarded - CHECK (j_array == json({1, 2, 4, 5})); - } - } - } - - SECTION("special cases") - { - // the following test cases cover the situation in which an empty - // object and array is discarded only after the closing character - // has been read - - json j_empty_object = json::parse("{}", [](int, json::parse_event_t e, const json&) - { - if (e == json::parse_event_t::object_end) - { - return false; - } - else - { - return true; - } - }); - CHECK(j_empty_object == json()); - - json j_empty_array = json::parse("[]", [](int, json::parse_event_t e, const json&) - { - if (e == json::parse_event_t::array_end) - { - return false; - } - else - { - return true; - } - }); - CHECK(j_empty_array == json()); - } - } - - SECTION("copy constructor") - { - json::string_t* s = new json::string_t("[1,2,3,4]"); - json::parser p(*s); - delete s; - CHECK(p.parse() == json({1, 2, 3, 4})); - } -} - -TEST_CASE("README", "[hide]") -{ - { - // redirect std::cout for the README file - auto old_cout_buffer = std::cout.rdbuf(); - std::ostringstream new_stream; - std::cout.rdbuf(new_stream.rdbuf()); - { - // create an empty structure (null) - json j; - - // add a number that is stored as double (note the implicit conversion of j to an object) - j["pi"] = 3.141; - - // add a Boolean that is stored as bool - j["happy"] = true; - - // add a string that is stored as std::string - j["name"] = "Niels"; - - // add another null object by passing nullptr - j["nothing"] = nullptr; - - // add an object inside the object - j["answer"]["everything"] = 42; - - // add an array that is stored as std::vector (using an initializer list) - j["list"] = { 1, 0, 2 }; - - // add another object (using an initializer list of pairs) - j["object"] = { {"currency", "USD"}, {"value", 42.99} }; - - // instead, you could also write (which looks very similar to the JSON above) - json j2 = - { - {"pi", 3.141}, - {"happy", true}, - {"name", "Niels"}, - {"nothing", nullptr}, - { - "answer", { - {"everything", 42} - } - }, - {"list", {1, 0, 2}}, - { - "object", { - {"currency", "USD"}, - {"value", 42.99} - } - } - }; - } - - { - // ways to express the empty array [] - json empty_array_implicit = {{}}; - json empty_array_explicit = json::array(); - - // a way to express the empty object {} - json empty_object_explicit = json::object(); - - // a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]] - json array_not_object = { json::array({"currency", "USD"}), json::array({"value", 42.99}) }; - } - - { - // create object from string literal - json j = "{ \"happy\": true, \"pi\": 3.141 }"_json; - - // or even nicer with a raw string literal - auto j2 = R"( - { - "happy": true, - "pi": 3.141 - } - )"_json; - - // or explicitly - auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }"); - - // explicit conversion to string - std::string s = j.dump(); // {\"happy\":true,\"pi\":3.141} - - // serialization with pretty printing - // pass in the amount of spaces to indent - std::cout << j.dump(4) << std::endl; - // { - // "happy": true, - // "pi": 3.141 - // } - - std::cout << std::setw(2) << j << std::endl; - } - - { - // create an array using push_back - json j; - j.push_back("foo"); - j.push_back(1); - j.push_back(true); - - // iterate the array - for (json::iterator it = j.begin(); it != j.end(); ++it) - { - std::cout << *it << '\n'; - } - - // range-based for - for (auto element : j) - { - std::cout << element << '\n'; - } - - // getter/setter - const std::string tmp = j[0]; - j[1] = 42; - bool foo = j.at(2); - - // other stuff - j.size(); // 3 entries - j.empty(); // false - j.type(); // json::value_t::array - j.clear(); // the array is empty again - - // comparison - j == "[\"foo\", 1, true]"_json; // true - - // create an object - json o; - o["foo"] = 23; - o["bar"] = false; - o["baz"] = 3.141; - - // find an entry - if (o.find("foo") != o.end()) - { - // there is an entry with key "foo" - } - } - - { - std::vector c_vector {1, 2, 3, 4}; - json j_vec(c_vector); - // [1, 2, 3, 4] - - std::deque c_deque {1.2f, 2.3f, 3.4f, 5.6f}; - json j_deque(c_deque); - // [1.2, 2.3, 3.4, 5.6] - - std::list c_list {true, true, false, true}; - json j_list(c_list); - // [true, true, false, true] - - std::forward_list c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543}; - json j_flist(c_flist); - // [12345678909876, 23456789098765, 34567890987654, 45678909876543] - - std::array c_array {{1, 2, 3, 4}}; - json j_array(c_array); - // [1, 2, 3, 4] - - std::set c_set {"one", "two", "three", "four", "one"}; - json j_set(c_set); // only one entry for "one" is used - // ["four", "one", "three", "two"] - - std::unordered_set c_uset {"one", "two", "three", "four", "one"}; - json j_uset(c_uset); // only one entry for "one" is used - // maybe ["two", "three", "four", "one"] - - std::multiset c_mset {"one", "two", "one", "four"}; - json j_mset(c_mset); // only one entry for "one" is used - // maybe ["one", "two", "four"] - - std::unordered_multiset c_umset {"one", "two", "one", "four"}; - json j_umset(c_umset); // both entries for "one" are used - // maybe ["one", "two", "one", "four"] - } - - { - std::map c_map { {"one", 1}, {"two", 2}, {"three", 3} }; - json j_map(c_map); - // {"one": 1, "two": 2, "three": 3} - - std::unordered_map c_umap { {"one", 1.2f}, {"two", 2.3f}, {"three", 3.4f} }; - json j_umap(c_umap); - // {"one": 1.2, "two": 2.3, "three": 3.4} - - std::multimap c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; - json j_mmap(c_mmap); // only one entry for key "three" is used - // maybe {"one": true, "two": true, "three": true} - - std::unordered_multimap c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; - json j_ummap(c_ummap); // only one entry for key "three" is used - // maybe {"one": true, "two": true, "three": true} - } - - { - // strings - std::string s1 = "Hello, world!"; - json js = s1; - std::string s2 = js; - - // Booleans - bool b1 = true; - json jb = b1; - bool b2 = jb; - - // numbers - int i = 42; - json jn = i; - double f = jn; - - // etc. - - std::string vs = js.get(); - bool vb = jb.get(); - int vi = jn.get(); - - // etc. - } - - { - // a JSON value - json j_original = R"({ - "baz": ["one", "two", "three"], - "foo": "bar" - })"_json; - - // access members with a JSON pointer (RFC 6901) - j_original["/baz/1"_json_pointer]; - // "two" - - // a JSON patch (RFC 6902) - json j_patch = R"([ - { "op": "replace", "path": "/baz", "value": "boo" }, - { "op": "add", "path": "/hello", "value": ["world"] }, - { "op": "remove", "path": "/foo"} - ])"_json; - - // apply the patch - json j_result = j_original.patch(j_patch); - // { - // "baz": "boo", - // "hello": ["world"] - // } - - // calculate a JSON patch from two JSON values - json::diff(j_result, j_original); - // [ - // { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] }, - // { "op":"remove","path":"/hello" }, - // { "op":"add","path":"/foo","value":"bar" } - // ] - } - - // restore old std::cout - std::cout.rdbuf(old_cout_buffer); - } -} - -TEST_CASE("algorithms") -{ - json j_array = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz"}; - json j_object = {{"one", 1}, {"two", 2}}; - - SECTION("non-modifying sequence operations") - { - SECTION("std::all_of") - { - CHECK(std::all_of(j_array.begin(), j_array.end(), [](const json & value) - { - return value.size() > 0; - })); - CHECK(std::all_of(j_object.begin(), j_object.end(), [](const json & value) - { - return value.type() == json::value_t::number_integer; - })); - } - - SECTION("std::any_of") - { - CHECK(std::any_of(j_array.begin(), j_array.end(), [](const json & value) - { - return value.is_string() and value.get() == "foo"; - })); - CHECK(std::any_of(j_object.begin(), j_object.end(), [](const json & value) - { - return value.get() > 1; - })); - } - - SECTION("std::none_of") - { - CHECK(std::none_of(j_array.begin(), j_array.end(), [](const json & value) - { - return value.size() == 0; - })); - CHECK(std::none_of(j_object.begin(), j_object.end(), [](const json & value) - { - return value.get() <= 0; - })); - } - - SECTION("std::for_each") - { - SECTION("reading") - { - int sum = 0; - - std::for_each(j_array.cbegin(), j_array.cend(), [&sum](const json & value) - { - if (value.is_number()) - { - sum += static_cast(value); - } - }); - - CHECK(sum == 45); - } - - SECTION("writing") - { - auto add17 = [](json & value) - { - if (value.is_array()) - { - value.push_back(17); - } - }; - - std::for_each(j_array.begin(), j_array.end(), add17); - - CHECK(j_array[6] == json({1, 2, 3, 17})); - } - } - - SECTION("std::count") - { - CHECK(std::count(j_array.begin(), j_array.end(), json(true)) == 1); - } - - SECTION("std::count_if") - { - CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json & value) - { - return (value.is_number()); - }) == 3); - CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json&) - { - return true; - }) == 9); - } - - SECTION("std::mismatch") - { - json j_array2 = {13, 29, 3, {{"one", 1}, {"two", 2}, {"three", 3}}, true, false, {1, 2, 3}, "foo", "baz"}; - auto res = std::mismatch(j_array.begin(), j_array.end(), j_array2.begin()); - CHECK(*res.first == json({{"one", 1}, {"two", 2}})); - CHECK(*res.second == json({{"one", 1}, {"two", 2}, {"three", 3}})); - } - - SECTION("std::equal") - { - SECTION("using operator==") - { - CHECK(std::equal(j_array.begin(), j_array.end(), j_array.begin())); - CHECK(std::equal(j_object.begin(), j_object.end(), j_object.begin())); - CHECK(not std::equal(j_array.begin(), j_array.end(), j_object.begin())); - } - - SECTION("using user-defined comparison") - { - // compare objects only by size of its elements - json j_array2 = {13, 29, 3, {"Hello", "World"}, true, false, {{"one", 1}, {"two", 2}, {"three", 3}}, "foo", "baz"}; - CHECK(not std::equal(j_array.begin(), j_array.end(), j_array2.begin())); - CHECK(std::equal(j_array.begin(), j_array.end(), j_array2.begin(), - [](const json & a, const json & b) - { - return (a.size() == b.size()); - })); - } - } - - SECTION("std::find") - { - auto it = std::find(j_array.begin(), j_array.end(), json(false)); - CHECK(std::distance(j_array.begin(), it) == 5); - } - - SECTION("std::find_if") - { - auto it = std::find_if(j_array.begin(), j_array.end(), - [](const json & value) - { - return value.is_boolean(); - }); - CHECK(std::distance(j_array.begin(), it) == 4); - } - - SECTION("std::find_if_not") - { - auto it = std::find_if_not(j_array.begin(), j_array.end(), - [](const json & value) - { - return value.is_number(); - }); - CHECK(std::distance(j_array.begin(), it) == 3); - } - - SECTION("std::adjacent_find") - { - CHECK(std::adjacent_find(j_array.begin(), j_array.end()) == j_array.end()); - CHECK(std::adjacent_find(j_array.begin(), j_array.end(), - [](const json & v1, const json & v2) - { - return v1.type() == v2.type(); - }) == j_array.begin()); - } - } - - SECTION("modifying sequence operations") - { - SECTION("std::reverse") - { - std::reverse(j_array.begin(), j_array.end()); - CHECK(j_array == json({"baz", "foo", {1, 2, 3}, false, true, {{"one", 1}, {"two", 2}}, 3, 29, 13})); - } - - SECTION("std::rotate") - { - std::rotate(j_array.begin(), j_array.begin() + 1, j_array.end()); - CHECK(j_array == json({29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", 13})); - } - - SECTION("std::partition") - { - auto it = std::partition(j_array.begin(), j_array.end(), [](const json & v) - { - return v.is_string(); - }); - CHECK(std::distance(j_array.begin(), it) == 2); - CHECK(not it[2].is_string()); - } - } - - SECTION("sorting operations") - { - SECTION("std::sort") - { - SECTION("with standard comparison") - { - json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr}; - std::sort(j.begin(), j.end()); - CHECK(j == json({nullptr, false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"})); - } - - SECTION("with user-defined comparison") - { - json j = {3, {{"one", 1}, {"two", 2}}, {1, 2, 3}, nullptr}; - std::sort(j.begin(), j.end(), [](const json & a, const json & b) - { - return a.size() < b.size(); - }); - CHECK(j == json({nullptr, 3, {{"one", 1}, {"two", 2}}, {1, 2, 3}})); - } - - SECTION("sorting an object") - { - json j({{"one", 1}, {"two", 2}}); - CHECK_THROWS_AS(std::sort(j.begin(), j.end()), std::domain_error); - CHECK_THROWS_WITH(std::sort(j.begin(), j.end()), "cannot use offsets with object iterators"); - } - } - - SECTION("std::partial_sort") - { - json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr}; - std::partial_sort(j.begin(), j.begin() + 4, j.end()); - CHECK(j == json({nullptr, false, true, 3, {{"one", 1}, {"two", 2}}, 29, {1, 2, 3}, "foo", "baz", 13})); - } - } - - SECTION("set operations") - { - SECTION("std::merge") - { - { - json j1 = {2, 4, 6, 8}; - json j2 = {1, 2, 3, 5, 7}; - json j3; - - std::merge(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); - CHECK(j3 == json({1, 2, 2, 3, 4, 5, 6, 7, 8})); - } - } - - SECTION("std::set_difference") - { - json j1 = {1, 2, 3, 4, 5, 6, 7, 8}; - json j2 = {1, 2, 3, 5, 7}; - json j3; - - std::set_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); - CHECK(j3 == json({4, 6, 8})); - } - - SECTION("std::set_intersection") - { - json j1 = {1, 2, 3, 4, 5, 6, 7, 8}; - json j2 = {1, 2, 3, 5, 7}; - json j3; - - std::set_intersection(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); - CHECK(j3 == json({1, 2, 3, 5, 7})); - } - - SECTION("std::set_union") - { - json j1 = {2, 4, 6, 8}; - json j2 = {1, 2, 3, 5, 7}; - json j3; - - std::set_union(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); - CHECK(j3 == json({1, 2, 3, 4, 5, 6, 7, 8})); - } - - SECTION("std::set_symmetric_difference") - { - json j1 = {2, 4, 6, 8}; - json j2 = {1, 2, 3, 5, 7}; - json j3; - - std::set_symmetric_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); - CHECK(j3 == json({1, 3, 4, 5, 6, 7, 8})); - } - } - - SECTION("heap operations") - { - std::make_heap(j_array.begin(), j_array.end()); - CHECK(std::is_heap(j_array.begin(), j_array.end())); - std::sort_heap(j_array.begin(), j_array.end()); - CHECK(j_array == json({false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"})); - } -} - -TEST_CASE("concepts") -{ - SECTION("container requirements for json") - { - // X: container class: json - // T: type of objects: json - // a, b: values of type X: json - - // TABLE 96 - Container Requirements - - // X::value_type must return T - CHECK((std::is_same::value)); - - // X::reference must return lvalue of T - CHECK((std::is_same::value)); - - // X::const_reference must return const lvalue of T - CHECK((std::is_same::value)); - - // X::iterator must return iterator whose value_type is T - CHECK((std::is_same::value)); - // X::iterator must meet the forward iterator requirements - CHECK((std::is_base_of::iterator_category>::value)); - // X::iterator must be convertible to X::const_iterator - CHECK((std::is_convertible::value)); - - // X::const_iterator must return iterator whose value_type is T - CHECK((std::is_same::value)); - // X::const_iterator must meet the forward iterator requirements - CHECK((std::is_base_of::iterator_category>::value)); - - // X::difference_type must return a signed integer - CHECK((std::is_signed::value)); - // X::difference_type must be identical to X::iterator::difference_type - CHECK((std::is_same::value)); - // X::difference_type must be identical to X::const_iterator::difference_type - CHECK((std::is_same::value)); - - // X::size_type must return an unsigned integer - CHECK((std::is_unsigned::value)); - // X::size_type can represent any non-negative value of X::difference_type - CHECK(std::numeric_limits::max() <= - std::numeric_limits::max()); - - // the expression "X u" has the post-condition "u.empty()" - { - json u; - CHECK(u.empty()); - } - - // the expression "X()" has the post-condition "X().empty()" - CHECK(json().empty()); - } - - SECTION("class json") - { - SECTION("DefaultConstructible") - { - CHECK(std::is_nothrow_default_constructible::value); - } - - SECTION("MoveConstructible") - { - CHECK(std::is_nothrow_move_constructible::value); - } - - SECTION("CopyConstructible") - { - CHECK(std::is_copy_constructible::value); - } - - SECTION("MoveAssignable") - { - CHECK(std::is_nothrow_move_assignable::value); - } - - SECTION("CopyAssignable") - { - CHECK(std::is_copy_assignable::value); - } - - SECTION("Destructible") - { - CHECK(std::is_nothrow_destructible::value); - } - - SECTION("StandardLayoutType") - { - CHECK(std::is_standard_layout::value); - } - } - - SECTION("class iterator") - { - SECTION("CopyConstructible") - { - CHECK(std::is_nothrow_copy_constructible::value); - CHECK(std::is_nothrow_copy_constructible::value); - } - - SECTION("CopyAssignable") - { - // STL iterators used by json::iterator don't pass this test in Debug mode -#if !defined(_MSC_VER) || (_ITERATOR_DEBUG_LEVEL == 0) - CHECK(std::is_nothrow_copy_assignable::value); - CHECK(std::is_nothrow_copy_assignable::value); -#endif - } - - SECTION("Destructible") - { - CHECK(std::is_nothrow_destructible::value); - CHECK(std::is_nothrow_destructible::value); - } - - SECTION("Swappable") - { - { - json j {1, 2, 3}; - json::iterator it1 = j.begin(); - json::iterator it2 = j.end(); - std::swap(it1, it2); - CHECK(it1 == j.end()); - CHECK(it2 == j.begin()); - } - { - json j {1, 2, 3}; - json::const_iterator it1 = j.cbegin(); - json::const_iterator it2 = j.cend(); - std::swap(it1, it2); - CHECK(it1 == j.end()); - CHECK(it2 == j.begin()); - } - } - } -} - -TEST_CASE("iterator_wrapper") -{ - SECTION("object") - { - SECTION("value") - { - json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("reference") - { - json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - - // change the value - i.value() = json(11); - CHECK(i.value() == json(11)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - - // change the value - i.value() = json(22); - CHECK(i.value() == json(22)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - - // check if values where changed - CHECK(j == json({{"A", 11}, {"B", 22}})); - } - - SECTION("const value") - { - json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (const auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("const reference") - { - json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (const auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - } - - SECTION("const object") - { - SECTION("value") - { - const json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("reference") - { - const json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("const value") - { - const json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (const auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("const reference") - { - const json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (const auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - } - - SECTION("array") - { - SECTION("value") - { - json j = {"A", "B"}; - int counter = 1; - - for (auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("reference") - { - json j = {"A", "B"}; - int counter = 1; - - for (auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - - // change the value - i.value() = "AA"; - CHECK(i.value() == "AA"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - - // change the value - i.value() = "BB"; - CHECK(i.value() == "BB"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - - // check if values where changed - CHECK(j == json({"AA", "BB"})); - } - - SECTION("const value") - { - json j = {"A", "B"}; - int counter = 1; - - for (const auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("const reference") - { - json j = {"A", "B"}; - int counter = 1; - - for (const auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - } - - SECTION("const array") - { - SECTION("value") - { - const json j = {"A", "B"}; - int counter = 1; - - for (auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("reference") - { - const json j = {"A", "B"}; - int counter = 1; - - for (auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("const value") - { - const json j = {"A", "B"}; - int counter = 1; - - for (const auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("const reference") - { - const json j = {"A", "B"}; - int counter = 1; - - for (const auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - } - - SECTION("primitive") - { - SECTION("value") - { - json j = 1; - int counter = 1; - - for (auto i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - } - - CHECK(counter == 2); - } - - SECTION("reference") - { - json j = 1; - int counter = 1; - - for (auto& i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - - // change value - i.value() = json(2); - } - - CHECK(counter == 2); - - // check if value has changed - CHECK(j == json(2)); - } - - SECTION("const value") - { - json j = 1; - int counter = 1; - - for (const auto i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - } - - CHECK(counter == 2); - } - - SECTION("const reference") - { - json j = 1; - int counter = 1; - - for (const auto& i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - } - - CHECK(counter == 2); - } - } - - SECTION("const primitive") - { - SECTION("value") - { - const json j = 1; - int counter = 1; - - for (auto i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - } - - CHECK(counter == 2); - } - - SECTION("reference") - { - const json j = 1; - int counter = 1; - - for (auto& i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - } - - CHECK(counter == 2); - } - - SECTION("const value") - { - const json j = 1; - int counter = 1; - - for (const auto i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - } - - CHECK(counter == 2); - } - - SECTION("const reference") - { - const json j = 1; - int counter = 1; - - for (const auto& i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - } - - CHECK(counter == 2); - } - } -} - -TEST_CASE("compliance tests from json.org") -{ - // test cases are from http://json.org/JSON_checker/ - - SECTION("expected failures") - { - for (auto filename : - { - //"test/data/json_tests/fail1.json", - "test/data/json_tests/fail2.json", - "test/data/json_tests/fail3.json", - "test/data/json_tests/fail4.json", - "test/data/json_tests/fail5.json", - "test/data/json_tests/fail6.json", - "test/data/json_tests/fail7.json", - "test/data/json_tests/fail8.json", - "test/data/json_tests/fail9.json", - "test/data/json_tests/fail10.json", - "test/data/json_tests/fail11.json", - "test/data/json_tests/fail12.json", - "test/data/json_tests/fail13.json", - "test/data/json_tests/fail14.json", - "test/data/json_tests/fail15.json", - "test/data/json_tests/fail16.json", - "test/data/json_tests/fail17.json", - //"test/data/json_tests/fail18.json", - "test/data/json_tests/fail19.json", - "test/data/json_tests/fail20.json", - "test/data/json_tests/fail21.json", - "test/data/json_tests/fail22.json", - "test/data/json_tests/fail23.json", - "test/data/json_tests/fail24.json", - "test/data/json_tests/fail25.json", - "test/data/json_tests/fail26.json", - "test/data/json_tests/fail27.json", - "test/data/json_tests/fail28.json", - "test/data/json_tests/fail29.json", - "test/data/json_tests/fail30.json", - "test/data/json_tests/fail31.json", - "test/data/json_tests/fail32.json", - "test/data/json_tests/fail33.json" - }) - { - CAPTURE(filename); - json j; - std::ifstream f(filename); - CHECK_THROWS_AS(j << f, std::invalid_argument); - } - } - - SECTION("expected passes") - { - for (auto filename : - { - "test/data/json_tests/pass1.json", - "test/data/json_tests/pass2.json", - "test/data/json_tests/pass3.json" - }) - { - CAPTURE(filename); - json j; - std::ifstream f(filename); - CHECK_NOTHROW(j << f); - } - } -} - -TEST_CASE("compliance tests from nativejson-benchmark") -{ - // test cases from https://github.com/miloyip/nativejson-benchmark/blob/master/src/main.cpp - - SECTION("doubles") - { - auto TEST_DOUBLE = [](const std::string & json_string, const double expected) - { - CAPTURE(json_string); - CAPTURE(expected); - CHECK(json::parse(json_string)[0].get() == Approx(expected)); - }; - - TEST_DOUBLE("[0.0]", 0.0); - TEST_DOUBLE("[-0.0]", -0.0); - TEST_DOUBLE("[1.0]", 1.0); - TEST_DOUBLE("[-1.0]", -1.0); - TEST_DOUBLE("[1.5]", 1.5); - TEST_DOUBLE("[-1.5]", -1.5); - TEST_DOUBLE("[3.1416]", 3.1416); - TEST_DOUBLE("[1E10]", 1E10); - TEST_DOUBLE("[1e10]", 1e10); - TEST_DOUBLE("[1E+10]", 1E+10); - TEST_DOUBLE("[1E-10]", 1E-10); - TEST_DOUBLE("[-1E10]", -1E10); - TEST_DOUBLE("[-1e10]", -1e10); - TEST_DOUBLE("[-1E+10]", -1E+10); - TEST_DOUBLE("[-1E-10]", -1E-10); - TEST_DOUBLE("[1.234E+10]", 1.234E+10); - TEST_DOUBLE("[1.234E-10]", 1.234E-10); - TEST_DOUBLE("[1.79769e+308]", 1.79769e+308); - TEST_DOUBLE("[2.22507e-308]", 2.22507e-308); - TEST_DOUBLE("[-1.79769e+308]", -1.79769e+308); - TEST_DOUBLE("[-2.22507e-308]", -2.22507e-308); - TEST_DOUBLE("[4.9406564584124654e-324]", 4.9406564584124654e-324); // minimum denormal - TEST_DOUBLE("[2.2250738585072009e-308]", 2.2250738585072009e-308); // Max subnormal double - TEST_DOUBLE("[2.2250738585072014e-308]", 2.2250738585072014e-308); // Min normal positive double - TEST_DOUBLE("[1.7976931348623157e+308]", 1.7976931348623157e+308); // Max double - TEST_DOUBLE("[1e-10000]", 0.0); // must underflow - TEST_DOUBLE("[18446744073709551616]", - 18446744073709551616.0); // 2^64 (max of uint64_t + 1, force to use double) - TEST_DOUBLE("[-9223372036854775809]", - -9223372036854775809.0); // -2^63 - 1(min of int64_t + 1, force to use double) - TEST_DOUBLE("[0.9868011474609375]", - 0.9868011474609375); // https://github.com/miloyip/rapidjson/issues/120 - TEST_DOUBLE("[123e34]", 123e34); // Fast Path Cases In Disguise - TEST_DOUBLE("[45913141877270640000.0]", 45913141877270640000.0); - TEST_DOUBLE("[2.2250738585072011e-308]", - 2.2250738585072011e-308); - //TEST_DOUBLE("[1e-00011111111111]", 0.0); - //TEST_DOUBLE("[-1e-00011111111111]", -0.0); - TEST_DOUBLE("[1e-214748363]", 0.0); - TEST_DOUBLE("[1e-214748364]", 0.0); - //TEST_DOUBLE("[1e-21474836311]", 0.0); - TEST_DOUBLE("[0.017976931348623157e+310]", 1.7976931348623157e+308); // Max double in another form - - // Since - // abs((2^-1022 - 2^-1074) - 2.2250738585072012e-308) = 3.109754131239141401123495768877590405345064751974375599... ¡Á 10^-324 - // abs((2^-1022) - 2.2250738585072012e-308) = 1.830902327173324040642192159804623318305533274168872044... ¡Á 10 ^ -324 - // So 2.2250738585072012e-308 should round to 2^-1022 = 2.2250738585072014e-308 - TEST_DOUBLE("[2.2250738585072012e-308]", - 2.2250738585072014e-308); - - // More closer to normal/subnormal boundary - // boundary = 2^-1022 - 2^-1075 = 2.225073858507201136057409796709131975934819546351645648... ¡Á 10^-308 - TEST_DOUBLE("[2.22507385850720113605740979670913197593481954635164564e-308]", - 2.2250738585072009e-308); - TEST_DOUBLE("[2.22507385850720113605740979670913197593481954635164565e-308]", - 2.2250738585072014e-308); - - // 1.0 is in (1.0 - 2^-54, 1.0 + 2^-53) - // 1.0 - 2^-54 = 0.999999999999999944488848768742172978818416595458984375 - TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984375]", 1.0); // round to even - TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984374]", - 0.99999999999999989); // previous double - TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984376]", 1.0); // next double - // 1.0 + 2^-53 = 1.00000000000000011102230246251565404236316680908203125 - TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203125]", 1.0); // round to even - TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203124]", 1.0); // previous double - TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203126]", - 1.00000000000000022); // next double - - // Numbers from https://github.com/floitsch/double-conversion/blob/master/test/cctest/test-strtod.cc - - TEST_DOUBLE("[72057594037927928.0]", 72057594037927928.0); - TEST_DOUBLE("[72057594037927936.0]", 72057594037927936.0); - TEST_DOUBLE("[72057594037927932.0]", 72057594037927936.0); - TEST_DOUBLE("[7205759403792793199999e-5]", 72057594037927928.0); - TEST_DOUBLE("[7205759403792793200001e-5]", 72057594037927936.0); - - TEST_DOUBLE("[9223372036854774784.0]", 9223372036854774784.0); - TEST_DOUBLE("[9223372036854775808.0]", 9223372036854775808.0); - TEST_DOUBLE("[9223372036854775296.0]", 9223372036854775808.0); - TEST_DOUBLE("[922337203685477529599999e-5]", 9223372036854774784.0); - TEST_DOUBLE("[922337203685477529600001e-5]", 9223372036854775808.0); - - TEST_DOUBLE("[10141204801825834086073718800384]", 10141204801825834086073718800384.0); - TEST_DOUBLE("[10141204801825835211973625643008]", 10141204801825835211973625643008.0); - TEST_DOUBLE("[10141204801825834649023672221696]", 10141204801825835211973625643008.0); - TEST_DOUBLE("[1014120480182583464902367222169599999e-5]", 10141204801825834086073718800384.0); - TEST_DOUBLE("[1014120480182583464902367222169600001e-5]", 10141204801825835211973625643008.0); - - TEST_DOUBLE("[5708990770823838890407843763683279797179383808]", - 5708990770823838890407843763683279797179383808.0); - TEST_DOUBLE("[5708990770823839524233143877797980545530986496]", - 5708990770823839524233143877797980545530986496.0); - TEST_DOUBLE("[5708990770823839207320493820740630171355185152]", - 5708990770823839524233143877797980545530986496.0); - TEST_DOUBLE("[5708990770823839207320493820740630171355185151999e-3]", - 5708990770823838890407843763683279797179383808.0); - TEST_DOUBLE("[5708990770823839207320493820740630171355185152001e-3]", - 5708990770823839524233143877797980545530986496.0); - - { - char n1e308[312]; // '1' followed by 308 '0' - n1e308[0] = '['; - n1e308[1] = '1'; - for (int j = 2; j < 310; j++) - { - n1e308[j] = '0'; - } - n1e308[310] = ']'; - n1e308[311] = '\0'; - TEST_DOUBLE(n1e308, 1E308); - } - - // Cover trimming - TEST_DOUBLE( - "[2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508" - "7914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012" - "9811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306" - "6665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505" - "1080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621" - "5722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844" - "2390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042" - "7567186443383770486037861622771738545623065874679014086723327636718751234567890123456789012345678901" - "e-308]", - 2.2250738585072014e-308); - } - - SECTION("strings") - { - auto TEST_STRING = [](const std::string & json_string, const std::string & expected) - { - CAPTURE(json_string); - CAPTURE(expected); - CHECK(json::parse(json_string)[0].get() == expected); - }; - - TEST_STRING("[\"\"]", ""); - TEST_STRING("[\"Hello\"]", "Hello"); - TEST_STRING("[\"Hello\\nWorld\"]", "Hello\nWorld"); - //TEST_STRING("[\"Hello\\u0000World\"]", "Hello\0World"); - TEST_STRING("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]", "\"\\/\b\f\n\r\t"); - TEST_STRING("[\"\\u0024\"]", "\x24"); // Dollar sign U+0024 - TEST_STRING("[\"\\u00A2\"]", "\xC2\xA2"); // Cents sign U+00A2 - TEST_STRING("[\"\\u20AC\"]", "\xE2\x82\xAC"); // Euro sign U+20AC - TEST_STRING("[\"\\uD834\\uDD1E\"]", "\xF0\x9D\x84\x9E"); // G clef sign U+1D11E - } - - SECTION("roundtrip") - { - // test cases are from https://github.com/miloyip/nativejson-benchmark/tree/master/test/data/roundtrip - - for (auto filename : - { - "test/data/json_roundtrip/roundtrip01.json", - "test/data/json_roundtrip/roundtrip02.json", - "test/data/json_roundtrip/roundtrip03.json", - "test/data/json_roundtrip/roundtrip04.json", - "test/data/json_roundtrip/roundtrip05.json", - "test/data/json_roundtrip/roundtrip06.json", - "test/data/json_roundtrip/roundtrip07.json", - "test/data/json_roundtrip/roundtrip08.json", - "test/data/json_roundtrip/roundtrip09.json", - "test/data/json_roundtrip/roundtrip10.json", - "test/data/json_roundtrip/roundtrip11.json", - "test/data/json_roundtrip/roundtrip12.json", - "test/data/json_roundtrip/roundtrip13.json", - "test/data/json_roundtrip/roundtrip14.json", - "test/data/json_roundtrip/roundtrip15.json", - "test/data/json_roundtrip/roundtrip16.json", - "test/data/json_roundtrip/roundtrip17.json", - "test/data/json_roundtrip/roundtrip18.json", - "test/data/json_roundtrip/roundtrip19.json", - "test/data/json_roundtrip/roundtrip20.json", - "test/data/json_roundtrip/roundtrip21.json", - "test/data/json_roundtrip/roundtrip22.json", - "test/data/json_roundtrip/roundtrip23.json", - //"test/data/json_roundtrip/roundtrip24.json", // roundtrip error - //"test/data/json_roundtrip/roundtrip25.json", // roundtrip error - //"test/data/json_roundtrip/roundtrip26.json", // roundtrip error - //"test/data/json_roundtrip/roundtrip27.json", // roundtrip error - //"test/data/json_roundtrip/roundtrip28.json", // roundtrip error - "test/data/json_roundtrip/roundtrip29.json", - //"test/data/json_roundtrip/roundtrip30.json", // roundtrip error - //"test/data/json_roundtrip/roundtrip31.json", // roundtrip error - "test/data/json_roundtrip/roundtrip32.json" - }) - { - CAPTURE(filename); - std::ifstream f(filename); - std::string json_string( (std::istreambuf_iterator(f) ), - (std::istreambuf_iterator()) ); - - json j = json::parse(json_string); - CHECK(j.dump() == json_string); - } - } -} - -TEST_CASE("test suite from json-test-suite") -{ - SECTION("read all sample.json") - { - // read a file with all unicode characters stored as single-character - // strings in a JSON array - std::ifstream f("test/data/json_testsuite/sample.json"); - json j; - CHECK_NOTHROW(j << f); - - // the array has 3 elements - CHECK(j.size() == 3); - } -} - -TEST_CASE("json.org examples") -{ - // here, we list all JSON values from http://json.org/example - - SECTION("1.json") - { - std::ifstream f("test/data/json.org/1.json"); - json j; - CHECK_NOTHROW(j << f); - } - - SECTION("2.json") - { - std::ifstream f("test/data/json.org/2.json"); - json j; - CHECK_NOTHROW(j << f); - } - - SECTION("3.json") - { - std::ifstream f("test/data/json.org/3.json"); - json j; - CHECK_NOTHROW(j << f); - } - - SECTION("4.json") - { - std::ifstream f("test/data/json.org/4.json"); - json j; - CHECK_NOTHROW(j << f); - } - - SECTION("5.json") - { - std::ifstream f("test/data/json.org/5.json"); - json j; - CHECK_NOTHROW(j << f); - } -} - -TEST_CASE("RFC 7159 examples") -{ - // here, we list all JSON values from the RFC 7159 document - - SECTION("7. Strings") - { - CHECK(json::parse("\"\\u005C\"") == json("\\")); - CHECK(json::parse("\"\\uD834\\uDD1E\"") == json("𝄞")); - CHECK(json::parse("\"𝄞\"") == json("𝄞")); - } - - SECTION("8.3 String Comparison") - { - CHECK(json::parse("\"a\\b\"") == json::parse("\"a\u005Cb\"")); - } - - SECTION("13 Examples") - { - { - CHECK_NOTHROW(json(R"( - { - "Image": { - "Width": 800, - "Height": 600, - "Title": "View from 15th Floor", - "Thumbnail": { - "Url": "http://www.example.com/image/481989943", - "Height": 125, - "Width": 100 - }, - "Animated" : false, - "IDs": [116, 943, 234, 38793] - } - } - )")); - } - - { - CHECK_NOTHROW(json(R"( - [ - { - "precision": "zip", - "Latitude": 37.7668, - "Longitude": -122.3959, - "Address": "", - "City": "SAN FRANCISCO", - "State": "CA", - "Zip": "94107", - "Country": "US" - }, - { - "precision": "zip", - "Latitude": 37.371991, - "Longitude": -122.026020, - "Address": "", - "City": "SUNNYVALE", - "State": "CA", - "Zip": "94085", - "Country": "US" - } - ])")); - } - - CHECK(json::parse("\"Hello world!\"") == json("Hello world!")); - CHECK(json::parse("42") == json(42)); - CHECK(json::parse("true") == json(true)); - } -} - -TEST_CASE("Unicode", "[hide]") -{ - SECTION("full enumeration of Unicode code points") - { - // create an escaped string from a code point - const auto codepoint_to_unicode = [](std::size_t cp) - { - // copd points are represented as a six-character sequence: a - // reverse solidus, followed by the lowercase letter u, followed - // by four hexadecimal digits that encode the character's code - // point - std::stringstream ss; - ss << "\\u" << std::setw(4) << std::setfill('0') << std::hex << cp; - return ss.str(); - }; - - // generate all UTF-8 code points; in total, 1112064 code points are - // generated: 0x1FFFFF code points - 2048 invalid values between - // 0xD800 and 0xDFFF. - for (std::size_t cp = 0; cp <= 0x10FFFFu; ++cp) - { - // The Unicode standard permanently reserves these code point - // values for UTF-16 encoding of the high and low surrogates, and - // they will never be assigned a character, so there should be no - // reason to encode them. The official Unicode standard says that - // no UTF forms, including UTF-16, can encode these code points. - if (cp >= 0xD800u and cp <= 0xDFFFu) - { - // if we would not skip these code points, we would get a - // "missing low surrogate" exception - continue; - } - - // string to store the code point as in \uxxxx format - std::string escaped_string; - // string to store the code point as unescaped character sequence - std::string unescaped_string; - - if (cp < 0x10000u) - { - // code points in the Basic Multilingual Plane can be - // represented with one \\uxxxx sequence - escaped_string = codepoint_to_unicode(cp); - - // All Unicode characters may be placed within the quotation - // marks, except for the characters that must be escaped: - // quotation mark, reverse solidus, and the control characters - // (U+0000 through U+001F); we ignore these code points as - // they are checked with codepoint_to_unicode. - if (cp > 0x1f and cp != 0x22 and cp != 0x5c) - { - unescaped_string = json::lexer::to_unicode(cp); - } - } - else - { - // To escape an extended character that is not in the Basic - // Multilingual Plane, the character is represented as a - // 12-character sequence, encoding the UTF-16 surrogate pair - const auto codepoint1 = 0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu); - const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu); - escaped_string = codepoint_to_unicode(codepoint1); - escaped_string += codepoint_to_unicode(codepoint2); - unescaped_string += json::lexer::to_unicode(codepoint1, codepoint2); - } - - // all other code points are valid and must not yield parse errors - CAPTURE(cp); - CAPTURE(escaped_string); - CAPTURE(unescaped_string); - - json j1, j2, j3, j4; - CHECK_NOTHROW(j1 = json::parse("\"" + escaped_string + "\"")); - CHECK_NOTHROW(j2 = json::parse(j1.dump())); - CHECK(j1 == j2); - - CHECK_NOTHROW(j3 = json::parse("\"" + unescaped_string + "\"")); - CHECK_NOTHROW(j4 = json::parse(j3.dump())); - CHECK(j3 == j4); - } - } - - SECTION("read all unicode characters") - { - // read a file with all unicode characters stored as single-character - // strings in a JSON array - std::ifstream f("test/data/json_nlohmann_tests/all_unicode.json"); - json j; - CHECK_NOTHROW(j << f); - - // the array has 1112064 + 1 elemnts (a terminating "null" value) - // Note: 1112064 = 0x1FFFFF code points - 2048 invalid values between - // 0xD800 and 0xDFFF. - CHECK(j.size() == 1112065); - - SECTION("check JSON Pointers") - { - for (auto s : j) - { - // skip non-string JSON values - if (not s.is_string()) - { - continue; - } - - std::string ptr = s; - - // tilde must be followed by 0 or 1 - if (ptr == "~") - { - ptr += "0"; - } - - // JSON Pointers must begin with "/" - ptr = "/" + ptr; - - CHECK_NOTHROW(json::json_pointer("/" + ptr)); - - // check escape/unescape roundtrip - auto escaped = json::json_pointer::escape(ptr); - json::json_pointer::unescape(escaped); - CHECK(escaped == ptr); - } - } - } - - SECTION("ignore byte-order-mark") - { - // read a file with a UTF-8 BOM - std::ifstream f("test/data/json_nlohmann_tests/bom.json"); - json j; - CHECK_NOTHROW(j << f); - } - - SECTION("error for incomplete/wrong BOM") - { - CHECK_THROWS_AS(json::parse("\xef\xbb"), std::invalid_argument); - CHECK_THROWS_AS(json::parse("\xef\xbb\xbb"), std::invalid_argument); - } -} - -TEST_CASE("JSON pointers") -{ - SECTION("errors") - { - CHECK_THROWS_AS(json::json_pointer("foo"), std::domain_error); - CHECK_THROWS_WITH(json::json_pointer("foo"), "JSON pointer must be empty or begin with '/'"); - - CHECK_THROWS_AS(json::json_pointer("/~~"), std::domain_error); - CHECK_THROWS_WITH(json::json_pointer("/~~"), "escape error: '~' must be followed with '0' or '1'"); - - CHECK_THROWS_AS(json::json_pointer("/~"), std::domain_error); - CHECK_THROWS_WITH(json::json_pointer("/~"), "escape error: '~' must be followed with '0' or '1'"); - - json::json_pointer p; - CHECK_THROWS_AS(p.top(), std::domain_error); - CHECK_THROWS_WITH(p.top(), "JSON pointer has no parent"); - CHECK_THROWS_AS(p.pop_back(), std::domain_error); - CHECK_THROWS_WITH(p.pop_back(), "JSON pointer has no parent"); - } - - SECTION("examples from RFC 6901") - { - SECTION("nonconst access") - { - json j = R"( - { - "foo": ["bar", "baz"], - "": 0, - "a/b": 1, - "c%d": 2, - "e^f": 3, - "g|h": 4, - "i\\j": 5, - "k\"l": 6, - " ": 7, - "m~n": 8 - } - )"_json; - - // the whole document - CHECK(j[json::json_pointer()] == j); - CHECK(j[json::json_pointer("")] == j); - - // array access - CHECK(j[json::json_pointer("/foo")] == j["foo"]); - CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]); - CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]); - CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); - - // checked array access - CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]); - CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]); - - // empty string access - CHECK(j[json::json_pointer("/")] == j[""]); - - // other cases - CHECK(j[json::json_pointer("/ ")] == j[" "]); - CHECK(j[json::json_pointer("/c%d")] == j["c%d"]); - CHECK(j[json::json_pointer("/e^f")] == j["e^f"]); - CHECK(j[json::json_pointer("/g|h")] == j["g|h"]); - CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]); - CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]); - - // checked access - CHECK(j.at(json::json_pointer("/ ")) == j[" "]); - CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]); - CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]); - CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]); - CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]); - CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]); - - // escaped access - CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]); - CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]); - - // unescaped access - CHECK_THROWS_AS(j[json::json_pointer("/a/b")], std::out_of_range); - CHECK_THROWS_WITH(j[json::json_pointer("/a/b")], "unresolved reference token 'b'"); - // "/a/b" works for JSON {"a": {"b": 42}} - CHECK(json({{"a", {{"b", 42}}}})[json::json_pointer("/a/b")] == json(42)); - - // unresolved access - json j_primitive = 1; - CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range); - CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'"); - CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'"); - } - - SECTION("const access") - { - const json j = R"( - { - "foo": ["bar", "baz"], - "": 0, - "a/b": 1, - "c%d": 2, - "e^f": 3, - "g|h": 4, - "i\\j": 5, - "k\"l": 6, - " ": 7, - "m~n": 8 - } - )"_json; - - // the whole document - CHECK(j[json::json_pointer()] == j); - CHECK(j[json::json_pointer("")] == j); - - // array access - CHECK(j[json::json_pointer("/foo")] == j["foo"]); - CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]); - CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]); - CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); - - // checked array access - CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]); - CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]); - - // empty string access - CHECK(j[json::json_pointer("/")] == j[""]); - - // other cases - CHECK(j[json::json_pointer("/ ")] == j[" "]); - CHECK(j[json::json_pointer("/c%d")] == j["c%d"]); - CHECK(j[json::json_pointer("/e^f")] == j["e^f"]); - CHECK(j[json::json_pointer("/g|h")] == j["g|h"]); - CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]); - CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]); - - // checked access - CHECK(j.at(json::json_pointer("/ ")) == j[" "]); - CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]); - CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]); - CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]); - CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]); - CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]); - - // escaped access - CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]); - CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]); - - // unescaped access - CHECK_THROWS_AS(j.at(json::json_pointer("/a/b")), std::out_of_range); - CHECK_THROWS_WITH(j.at(json::json_pointer("/a/b")), "key 'a' not found"); - - // unresolved access - const json j_primitive = 1; - CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range); - CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'"); - CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'"); - } - - SECTION("user-defined string literal") - { - json j = R"( - { - "foo": ["bar", "baz"], - "": 0, - "a/b": 1, - "c%d": 2, - "e^f": 3, - "g|h": 4, - "i\\j": 5, - "k\"l": 6, - " ": 7, - "m~n": 8 - } - )"_json; - - // the whole document - CHECK(j[""_json_pointer] == j); - - // array access - CHECK(j["/foo"_json_pointer] == j["foo"]); - CHECK(j["/foo/0"_json_pointer] == j["foo"][0]); - CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); - } - } - - SECTION("array access") - { - SECTION("nonconst access") - { - json j = {1, 2, 3}; - const json j_const = j; - - // check reading access - CHECK(j["/0"_json_pointer] == j[0]); - CHECK(j["/1"_json_pointer] == j[1]); - CHECK(j["/2"_json_pointer] == j[2]); - - // assign to existing index - j["/1"_json_pointer] = 13; - CHECK(j[1] == json(13)); - - // assign to nonexisting index - j["/3"_json_pointer] = 33; - CHECK(j[3] == json(33)); - - // assign to nonexisting index (with gap) - j["/5"_json_pointer] = 55; - CHECK(j == json({1, 13, 3, 33, nullptr, 55})); - - // error with leading 0 - CHECK_THROWS_AS(j["/01"_json_pointer], std::domain_error); - CHECK_THROWS_WITH(j["/01"_json_pointer], "array index must not begin with '0'"); - CHECK_THROWS_AS(j_const["/01"_json_pointer], std::domain_error); - CHECK_THROWS_WITH(j_const["/01"_json_pointer], "array index must not begin with '0'"); - CHECK_THROWS_AS(j.at("/01"_json_pointer), std::domain_error); - CHECK_THROWS_WITH(j.at("/01"_json_pointer), "array index must not begin with '0'"); - CHECK_THROWS_AS(j_const.at("/01"_json_pointer), std::domain_error); - CHECK_THROWS_WITH(j_const.at("/01"_json_pointer), "array index must not begin with '0'"); - - // error with incorrect numbers - CHECK_THROWS_AS(j["/one"_json_pointer] = 1, std::invalid_argument); - - // assign to "-" - j["/-"_json_pointer] = 99; - CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99})); - - // error when using "-" in const object - CHECK_THROWS_AS(j_const["/-"_json_pointer], std::out_of_range); - CHECK_THROWS_WITH(j_const["/-"_json_pointer], "array index '-' (3) is out of range"); - - // error when using "-" with at - CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (7) is out of range"); - CHECK_THROWS_AS(j_const.at("/-"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j_const.at("/-"_json_pointer), "array index '-' (3) is out of range"); - } - - SECTION("const access") - { - const json j = {1, 2, 3}; - - // check reading access - CHECK(j["/0"_json_pointer] == j[0]); - CHECK(j["/1"_json_pointer] == j[1]); - CHECK(j["/2"_json_pointer] == j[2]); - - // assign to nonexisting index - CHECK_THROWS_AS(j.at("/3"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j.at("/3"_json_pointer), "array index 3 is out of range"); - - // assign to nonexisting index (with gap) - CHECK_THROWS_AS(j.at("/5"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j.at("/5"_json_pointer), "array index 5 is out of range"); - - // assign to "-" - CHECK_THROWS_AS(j["/-"_json_pointer], std::out_of_range); - CHECK_THROWS_WITH(j["/-"_json_pointer], "array index '-' (3) is out of range"); - CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (3) is out of range"); - } - - } - - SECTION("flatten") - { - json j = - { - {"pi", 3.141}, - {"happy", true}, - {"name", "Niels"}, - {"nothing", nullptr}, - { - "answer", { - {"everything", 42} - } - }, - {"list", {1, 0, 2}}, - { - "object", { - {"currency", "USD"}, - {"value", 42.99}, - {"", "empty string"}, - {"/", "slash"}, - {"~", "tilde"}, - {"~1", "tilde1"} - } - } - }; - - json j_flatten = - { - {"/pi", 3.141}, - {"/happy", true}, - {"/name", "Niels"}, - {"/nothing", nullptr}, - {"/answer/everything", 42}, - {"/list/0", 1}, - {"/list/1", 0}, - {"/list/2", 2}, - {"/object/currency", "USD"}, - {"/object/value", 42.99}, - {"/object/", "empty string"}, - {"/object/~1", "slash"}, - {"/object/~0", "tilde"}, - {"/object/~01", "tilde1"} - }; - - // check if flattened result is as expected - CHECK(j.flatten() == j_flatten); - - // check if unflattened result is as expected - CHECK(j_flatten.unflatten() == j); - - // error for nonobjects - CHECK_THROWS_AS(json(1).unflatten(), std::domain_error); - CHECK_THROWS_WITH(json(1).unflatten(), "only objects can be unflattened"); - - // error for nonprimitve values - CHECK_THROWS_AS(json({{"/1", {1, 2, 3}}}).unflatten(), std::domain_error); - CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(), "values in object must be primitive"); - - // error for conflicting values - json j_error = {{"", 42}, {"/foo", 17}}; - CHECK_THROWS_AS(j_error.unflatten(), std::domain_error); - CHECK_THROWS_WITH(j_error.unflatten(), "invalid value to unflatten"); - - // explicit roundtrip check - CHECK(j.flatten().unflatten() == j); - - // roundtrip for primitive values - json j_null; - CHECK(j_null.flatten().unflatten() == j_null); - json j_number = 42; - CHECK(j_number.flatten().unflatten() == j_number); - json j_boolean = false; - CHECK(j_boolean.flatten().unflatten() == j_boolean); - json j_string = "foo"; - CHECK(j_string.flatten().unflatten() == j_string); - - // roundtrip for empty structured values (will be unflattened to null) - json j_array(json::value_t::array); - CHECK(j_array.flatten().unflatten() == json()); - json j_object(json::value_t::object); - CHECK(j_object.flatten().unflatten() == json()); - } - - SECTION("string representation") - { - for (auto ptr : - {"", "/foo", "/foo/0", "/", "/a~1b", "/c%d", "/e^f", "/g|h", "/i\\j", "/k\"l", "/ ", "/m~0n" - }) - { - CHECK(json::json_pointer(ptr).to_string() == ptr); - } - } -} - -TEST_CASE("JSON patch") -{ - SECTION("examples from RFC 6902") - { - SECTION("4. Operations") - { - // the ordering of members in JSON objects is not significant: - json op1 = R"({ "op": "add", "path": "/a/b/c", "value": "foo" })"_json; - json op2 = R"({ "path": "/a/b/c", "op": "add", "value": "foo" })"_json; - json op3 = R"({ "value": "foo", "path": "/a/b/c", "op": "add" })"_json; - - // check if the operation objects are equivalent - CHECK(op1 == op2); - CHECK(op1 == op3); - } - - SECTION("4.1 add") - { - json patch = R"([{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }])"_json; - - // However, the object itself or an array containing it does need - // to exist, and it remains an error for that not to be the case. - // For example, an "add" with a target location of "/a/b" starting - // with this document - json doc1 = R"({ "a": { "foo": 1 } })"_json; - - // is not an error, because "a" exists, and "b" will be added to - // its value. - CHECK_NOTHROW(doc1.patch(patch)); - CHECK(doc1.patch(patch) == R"( - { - "a": { - "foo": 1, - "b": { - "c": [ "foo", "bar" ] - } - } - } - )"_json); - - // It is an error in this document: - json doc2 = R"({ "q": { "bar": 2 } })"_json; - - // because "a" does not exist. - CHECK_THROWS_AS(doc2.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(doc2.patch(patch), "key 'a' not found"); - } - - SECTION("4.2 remove") - { - // If removing an element from an array, any elements above the - // specified index are shifted one position to the left. - json doc = {1, 2, 3, 4}; - json patch = {{{"op", "remove"}, {"path", "/1"}}}; - CHECK(doc.patch(patch) == json({1, 3, 4})); - } - - SECTION("A.1. Adding an Object Member") - { - // An example target JSON document: - json doc = R"( - { "foo": "bar"} - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "/baz", "value": "qux" } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { - "baz": "qux", - "foo": "bar" - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.2. Adding an Array Element") - { - // An example target JSON document: - json doc = R"( - { "foo": [ "bar", "baz" ] } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "/foo/1", "value": "qux" } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { "foo": [ "bar", "qux", "baz" ] } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.3. Removing an Object Member") - { - // An example target JSON document: - json doc = R"( - { - "baz": "qux", - "foo": "bar" - } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "remove", "path": "/baz" } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { "foo": "bar" } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.4. Removing an Array Element") - { - // An example target JSON document: - json doc = R"( - { "foo": [ "bar", "qux", "baz" ] } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "remove", "path": "/foo/1" } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { "foo": [ "bar", "baz" ] } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.5. Replacing a Value") - { - // An example target JSON document: - json doc = R"( - { - "baz": "qux", - "foo": "bar" - } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "replace", "path": "/baz", "value": "boo" } - ] - )"_json; - - json expected = R"( - { - "baz": "boo", - "foo": "bar" - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.6. Moving a Value") - { - // An example target JSON document: - json doc = R"( - { - "foo": { - "bar": "baz", - "waldo": "fred" - }, - "qux": { - "corge": "grault" - } - } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "move", "from": "/foo/waldo", "path": "/qux/thud" } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { - "foo": { - "bar": "baz" - }, - "qux": { - "corge": "grault", - "thud": "fred" - } - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.7. Moving a Value") - { - // An example target JSON document: - json doc = R"( - { "foo": [ "all", "grass", "cows", "eat" ] } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "move", "from": "/foo/1", "path": "/foo/3" } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { "foo": [ "all", "cows", "eat", "grass" ] } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.8. Testing a Value: Success") - { - // An example target JSON document: - json doc = R"( - { - "baz": "qux", - "foo": [ "a", 2, "c" ] - } - )"_json; - - // A JSON Patch document that will result in successful evaluation: - json patch = R"( - [ - { "op": "test", "path": "/baz", "value": "qux" }, - { "op": "test", "path": "/foo/1", "value": 2 } - ] - )"_json; - - // check if evaluation does not throw - CHECK_NOTHROW(doc.patch(patch)); - // check if patched document is unchanged - CHECK(doc.patch(patch) == doc); - } - - SECTION("A.9. Testing a Value: Error") - { - // An example target JSON document: - json doc = R"( - { "baz": "qux" } - )"_json; - - // A JSON Patch document that will result in an error condition: - json patch = R"( - [ - { "op": "test", "path": "/baz", "value": "bar" } - ] - )"_json; - - // check that evaluation throws - CHECK_THROWS_AS(doc.patch(patch), std::domain_error); - CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); - } - - SECTION("A.10. Adding a Nested Member Object") - { - // An example target JSON document: - json doc = R"( - { "foo": "bar" } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "/child", "value": { "grandchild": { } } } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { - "foo": "bar", - "child": { - "grandchild": { - } - } - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.11. Ignoring Unrecognized Elements") - { - // An example target JSON document: - json doc = R"( - { "foo": "bar" } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "/baz", "value": "qux", "xyz": 123 } - ] - )"_json; - - json expected = R"( - { - "foo": "bar", - "baz": "qux" - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.12. Adding to a Nonexistent Target") - { - // An example target JSON document: - json doc = R"( - { "foo": "bar" } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "/baz/bat", "value": "qux" } - ] - )"_json; - - // This JSON Patch document, applied to the target JSON document - // above, would result in an error (therefore, it would not be - // applied), because the "add" operation's target location that - // references neither the root of the document, nor a member of - // an existing object, nor a member of an existing array. - - CHECK_THROWS_AS(doc.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(doc.patch(patch), "key 'baz' not found"); - } - - // A.13. Invalid JSON Patch Document - // not applicable - - SECTION("A.14. Escape Ordering") - { - // An example target JSON document: - json doc = R"( - { - "/": 9, - "~1": 10 - } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - {"op": "test", "path": "/~01", "value": 10} - ] - )"_json; - - json expected = R"( - { - "/": 9, - "~1": 10 - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.15. Comparing Strings and Numbers") - { - // An example target JSON document: - json doc = R"( - { - "/": 9, - "~1": 10 - } - )"_json; - - // A JSON Patch document that will result in an error condition: - json patch = R"( - [ - {"op": "test", "path": "/~01", "value": "10"} - ] - )"_json; - - // check that evaluation throws - CHECK_THROWS_AS(doc.patch(patch), std::domain_error); - CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); - } - - SECTION("A.16. Adding an Array Value") - { - // An example target JSON document: - json doc = R"( - { "foo": ["bar"] } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "/foo/-", "value": ["abc", "def"] } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { "foo": ["bar", ["abc", "def"]] } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - } - - SECTION("own examples") - { - SECTION("add") - { - SECTION("add to the root element") - { - // If the path is the root of the target document - the - // specified value becomes the entire content of the target - // document. - - // An example target JSON document: - json doc = 17; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "", "value": [1,2,3] } - ] - )"_json; - - // The resulting JSON document: - json expected = {1, 2, 3}; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("add to end of the array") - { - // The specified index MUST NOT be greater than the number of - // elements in the array. The example below uses and index of - // exactly the number of elements in the array which is legal. - - // An example target JSON document: - json doc = {0, 1, 2}; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "/3", "value": 3 } - ] - )"_json; - - // The resulting JSON document: - json expected = {0, 1, 2, 3}; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - } - - SECTION("copy") - { - // An example target JSON document: - json doc = R"( - { - "foo": { - "bar": "baz", - "waldo": "fred" - }, - "qux": { - "corge": "grault" - } - } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "copy", "from": "/foo/waldo", "path": "/qux/thud" } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { - "foo": { - "bar": "baz", - "waldo": "fred" - }, - "qux": { - "corge": "grault", - "thud": "fred" - } - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("replace") - { - json j = "string"; - json patch = {{{"op", "replace"}, {"path", ""}, {"value", 1}}}; - CHECK(j.patch(patch) == json(1)); - } - - SECTION("documentation GIF") - { - { - // a JSON patch - json p1 = R"( - [{"op": "add", "path": "/GB", "value": "London"}] - )"_json; - - // a JSON value - json source = R"( - {"D": "Berlin", "F": "Paris"} - )"_json; - - // apply the patch - json target = source.patch(p1); - // target = { "D": "Berlin", "F": "Paris", "GB": "London" } - CHECK(target == R"({ "D": "Berlin", "F": "Paris", "GB": "London" })"_json); - - // create a diff from two JSONs - json p2 = json::diff(target, source); - // p2 = [{"op": "delete", "path": "/GB"}] - CHECK(p2 == R"([{"op":"remove","path":"/GB"}])"_json); - } - { - // a JSON value - json j = {"good", "bad", "ugly"}; - - // a JSON pointer - auto ptr = json::json_pointer("/2"); - - // use to access elements - j[ptr] = {{"it", "cattivo"}}; - CHECK(j == R"(["good","bad",{"it":"cattivo"}])"_json); - - // use user-defined string literal - j["/2/en"_json_pointer] = "ugly"; - CHECK(j == R"(["good","bad",{"en":"ugly","it":"cattivo"}])"_json); - - json flat = j.flatten(); - CHECK(flat == R"({"/0":"good","/1":"bad","/2/en":"ugly","/2/it":"cattivo"})"_json); - } - } - } - - SECTION("errors") - { - SECTION("unknown operation") - { - SECTION("not an array") - { - json j; - json patch = {{"op", "add"}, {"path", ""}, {"value", 1}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects"); - } - - SECTION("not an array of objects") - { - json j; - json patch = {"op", "add", "path", "", "value", 1}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects"); - } - - SECTION("missing 'op'") - { - json j; - json patch = {{{"foo", "bar"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation must have member 'op'"); - } - - SECTION("non-string 'op'") - { - json j; - json patch = {{{"op", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation must have string member 'op'"); - } - - SECTION("invalid operation") - { - json j; - json patch = {{{"op", "foo"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation value 'foo' is invalid"); - } - } - - SECTION("add") - { - SECTION("missing 'path'") - { - json j; - json patch = {{{"op", "add"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'path'"); - } - - SECTION("non-string 'path'") - { - json j; - json patch = {{{"op", "add"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have string member 'path'"); - } - - SECTION("missing 'value'") - { - json j; - json patch = {{{"op", "add"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'value'"); - } - - SECTION("invalid array index") - { - json j = {1, 2}; - json patch = {{{"op", "add"}, {"path", "/4"}, {"value", 4}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 4 is out of range"); - } - } - - SECTION("remove") - { - SECTION("missing 'path'") - { - json j; - json patch = {{{"op", "remove"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have member 'path'"); - } - - SECTION("non-string 'path'") - { - json j; - json patch = {{{"op", "remove"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have string member 'path'"); - } - - SECTION("nonexisting target location (array)") - { - json j = {1, 2, 3}; - json patch = {{{"op", "remove"}, {"path", "/17"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range"); - } - - SECTION("nonexisting target location (object)") - { - json j = {{"foo", 1}, {"bar", 2}}; - json patch = {{{"op", "remove"}, {"path", "/baz"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); - } - - SECTION("root element as target location") - { - json j = "string"; - json patch = {{{"op", "remove"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::domain_error); - CHECK_THROWS_WITH(j.patch(patch), "JSON pointer has no parent"); - } - } - - SECTION("replace") - { - SECTION("missing 'path'") - { - json j; - json patch = {{{"op", "replace"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'path'"); - } - - SECTION("non-string 'path'") - { - json j; - json patch = {{{"op", "replace"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have string member 'path'"); - } - - SECTION("missing 'value'") - { - json j; - json patch = {{{"op", "replace"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'value'"); - } - - SECTION("nonexisting target location (array)") - { - json j = {1, 2, 3}; - json patch = {{{"op", "replace"}, {"path", "/17"}, {"value", 19}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range"); - } - - SECTION("nonexisting target location (object)") - { - json j = {{"foo", 1}, {"bar", 2}}; - json patch = {{{"op", "replace"}, {"path", "/baz"}, {"value", 3}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); - } - } - - SECTION("move") - { - SECTION("missing 'path'") - { - json j; - json patch = {{{"op", "move"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'path'"); - } - - SECTION("non-string 'path'") - { - json j; - json patch = {{{"op", "move"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'path'"); - } - - SECTION("missing 'from'") - { - json j; - json patch = {{{"op", "move"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'from'"); - } - - SECTION("non-string 'from'") - { - json j; - json patch = {{{"op", "move"}, {"path", ""}, {"from", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'from'"); - } - - SECTION("nonexisting from location (array)") - { - json j = {1, 2, 3}; - json patch = {{{"op", "move"}, {"path", "/0"}, {"from", "/5"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range"); - } - - SECTION("nonexisting from location (object)") - { - json j = {{"foo", 1}, {"bar", 2}}; - json patch = {{{"op", "move"}, {"path", "/baz"}, {"from", "/baz"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); - } - } - - SECTION("copy") - { - SECTION("missing 'path'") - { - json j; - json patch = {{{"op", "copy"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'path'"); - } - - SECTION("non-string 'path'") - { - json j; - json patch = {{{"op", "copy"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'path'"); - } - - SECTION("missing 'from'") - { - json j; - json patch = {{{"op", "copy"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'from'"); - } - - SECTION("non-string 'from'") - { - json j; - json patch = {{{"op", "copy"}, {"path", ""}, {"from", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'from'"); - } - - SECTION("nonexisting from location (array)") - { - json j = {1, 2, 3}; - json patch = {{{"op", "copy"}, {"path", "/0"}, {"from", "/5"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range"); - } - - SECTION("nonexisting from location (object)") - { - json j = {{"foo", 1}, {"bar", 2}}; - json patch = {{{"op", "copy"}, {"path", "/fob"}, {"from", "/baz"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); - } - } - - SECTION("test") - { - SECTION("missing 'path'") - { - json j; - json patch = {{{"op", "test"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'path'"); - } - - SECTION("non-string 'path'") - { - json j; - json patch = {{{"op", "test"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have string member 'path'"); - } - - SECTION("missing 'value'") - { - json j; - json patch = {{{"op", "test"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'value'"); - } - } - } - - SECTION("Examples from jsonpatch.com") - { - SECTION("Simple Example") - { - // The original document - json doc = R"( - { - "baz": "qux", - "foo": "bar" - } - )"_json; - - // The patch - json patch = R"( - [ - { "op": "replace", "path": "/baz", "value": "boo" }, - { "op": "add", "path": "/hello", "value": ["world"] }, - { "op": "remove", "path": "/foo"} - ] - )"_json; - - // The result - json result = R"( - { - "baz": "boo", - "hello": ["world"] - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == result); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, result)) == result); - } - - SECTION("Operations") - { - // The original document - json doc = R"( - { - "biscuits": [ - {"name":"Digestive"}, - {"name": "Choco Liebniz"} - ] - } - )"_json; - - SECTION("add") - { - // The patch - json patch = R"( - [ - {"op": "add", "path": "/biscuits/1", "value": {"name": "Ginger Nut"}} - ] - )"_json; - - // The result - json result = R"( - { - "biscuits": [ - {"name": "Digestive"}, - {"name": "Ginger Nut"}, - {"name": "Choco Liebniz"} - ] - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == result); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, result)) == result); - } - - SECTION("remove") - { - // The patch - json patch = R"( - [ - {"op": "remove", "path": "/biscuits"} - ] - )"_json; - - // The result - json result = R"( - {} - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == result); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, result)) == result); - } - - SECTION("replace") - { - // The patch - json patch = R"( - [ - {"op": "replace", "path": "/biscuits/0/name", "value": "Chocolate Digestive"} - ] - )"_json; - - // The result - json result = R"( - { - "biscuits": [ - {"name": "Chocolate Digestive"}, - {"name": "Choco Liebniz"} - ] - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == result); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, result)) == result); - } - - SECTION("copy") - { - // The patch - json patch = R"( - [ - {"op": "copy", "from": "/biscuits/0", "path": "/best_biscuit"} - ] - )"_json; - - // The result - json result = R"( - { - "biscuits": [ - {"name": "Digestive"}, - {"name": "Choco Liebniz"} - ], - "best_biscuit": { - "name": "Digestive" - } - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == result); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, result)) == result); - } - - SECTION("move") - { - // The patch - json patch = R"( - [ - {"op": "move", "from": "/biscuits", "path": "/cookies"} - ] - )"_json; - - // The result - json result = R"( - { - "cookies": [ - {"name": "Digestive"}, - {"name": "Choco Liebniz"} - ] - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == result); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, result)) == result); - } - - SECTION("test") - { - // The patch - json patch = R"( - [ - {"op": "test", "path": "/best_biscuit/name", "value": "Choco Liebniz"} - ] - )"_json; - - // the test will fail - CHECK_THROWS_AS(doc.patch(patch), std::domain_error); - CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); - } - } - } - - SECTION("Examples from bruth.github.io/jsonpatch-js") - { - SECTION("add") - { - CHECK(R"( {} )"_json.patch( - R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json - ) == R"( {"foo": "bar"} )"_json); - - CHECK(R"( {"foo": [1, 3]} )"_json.patch( - R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json - ) == R"( {"foo": "bar"} )"_json); - - CHECK(R"( {"foo": [{}]} )"_json.patch( - R"( [{"op": "add", "path": "/foo/0/bar", "value": "baz"}] )"_json - ) == R"( {"foo": [{"bar": "baz"}]} )"_json); - } - - SECTION("remove") - { - CHECK(R"( {"foo": "bar"} )"_json.patch( - R"( [{"op": "remove", "path": "/foo"}] )"_json - ) == R"( {} )"_json); - - CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( - R"( [{"op": "remove", "path": "/foo/1"}] )"_json - ) == R"( {"foo": [1, 3]} )"_json); - - CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch( - R"( [{"op": "remove", "path": "/foo/0/bar"}] )"_json - ) == R"( {"foo": [{}]} )"_json); - } - - SECTION("replace") - { - CHECK(R"( {"foo": "bar"} )"_json.patch( - R"( [{"op": "replace", "path": "/foo", "value": 1}] )"_json - ) == R"( {"foo": 1} )"_json); - - CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( - R"( [{"op": "replace", "path": "/foo/1", "value": 4}] )"_json - ) == R"( {"foo": [1, 4, 3]} )"_json); - - CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch( - R"( [{"op": "replace", "path": "/foo/0/bar", "value": 1}] )"_json - ) == R"( {"foo": [{"bar": 1}]} )"_json); - } - - SECTION("move") - { - CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( - R"( [{"op": "move", "from": "/foo", "path": "/bar"}] )"_json - ) == R"( {"bar": [1, 2, 3]} )"_json); - } - - SECTION("copy") - { - CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( - R"( [{"op": "copy", "from": "/foo/1", "path": "/bar"}] )"_json - ) == R"( {"foo": [1, 2, 3], "bar": 2} )"_json); - } - - SECTION("copy") - { - CHECK_NOTHROW(R"( {"foo": "bar"} )"_json.patch( - R"( [{"op": "test", "path": "/foo", "value": "bar"}] )"_json)); - } - } -} - -TEST_CASE("regression tests") -{ - SECTION("issue #60 - Double quotation mark is not parsed correctly") - { - SECTION("escape_dobulequote") - { - auto s = "[\"\\\"foo\\\"\"]"; - json j = json::parse(s); - auto expected = R"(["\"foo\""])"_json; - CHECK(j == expected); - } - } - - SECTION("issue #70 - Handle infinity and NaN cases") - { - SECTION("NAN value") - { - CHECK(json(NAN) == json()); - CHECK(json(json::number_float_t(NAN)) == json()); - } - - SECTION("infinity") - { - CHECK(json(INFINITY) == json()); - CHECK(json(json::number_float_t(INFINITY)) == json()); - } - } - - SECTION("pull request #71 - handle enum type") - { - enum { t = 0 }; - json j = json::array(); - j.push_back(t); - - j.push_back(json::object( - { - {"game_type", t} - })); - } - - SECTION("issue #76 - dump() / parse() not idempotent") - { - // create JSON object - json fields; - fields["one"] = std::string("one"); - fields["two"] = std::string("two three"); - fields["three"] = std::string("three \"four\""); - - // create another JSON object by deserializing the serialization - std::string payload = fields.dump(); - json parsed_fields = json::parse(payload); - - // check individual fields to match both objects - CHECK(parsed_fields["one"] == fields["one"]); - CHECK(parsed_fields["two"] == fields["two"]); - CHECK(parsed_fields["three"] == fields["three"]); - - // check individual fields to match original input - CHECK(parsed_fields["one"] == std::string("one")); - CHECK(parsed_fields["two"] == std::string("two three")); - CHECK(parsed_fields["three"] == std::string("three \"four\"")); - - // check equality of the objects - CHECK(parsed_fields == fields); - - // check equality of the serialized objects - CHECK(fields.dump() == parsed_fields.dump()); - - // check everything in one line - CHECK(fields == json::parse(fields.dump())); - } - - SECTION("issue #82 - lexer::get_number return NAN") - { - const auto content = R"( - { - "Test":"Test1", - "Number":100, - "Foo":42.42 - })"; - - std::stringstream ss; - ss << content; - json j; - ss >> j; - - std::string test = j["Test"]; - CHECK(test == "Test1"); - int number = j["Number"]; - CHECK(number == 100); - float foo = j["Foo"]; - CHECK(foo == Approx(42.42)); - } - - SECTION("issue #89 - nonstandard integer type") - { - // create JSON class with nonstandard integer number type - using custom_json = - nlohmann::basic_json; - custom_json j; - j["int_1"] = 1; - // we need to cast to int to compile with Catch - the value is int32_t - CHECK(static_cast(j["int_1"]) == 1); - - // tests for correct handling of non-standard integers that overflow the type selected by the user - - // unsigned integer object creation - expected to wrap and still be stored as an integer - j = 4294967296U; // 2^32 - CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_unsigned)); - CHECK(j.get() == 0); // Wrap - - // unsigned integer parsing - expected to overflow and be stored as a float - j = custom_json::parse("4294967296"); // 2^32 - CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_float)); - CHECK(j.get() == 4294967296.0f); - - // integer object creation - expected to wrap and still be stored as an integer - j = -2147483649LL; // -2^31-1 - CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_integer)); - CHECK(j.get() == 2147483647); // Wrap - - // integer parsing - expected to overflow and be stored as a float with rounding - j = custom_json::parse("-2147483649"); // -2^31 - CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_float)); - CHECK(j.get() == -2147483650.0f); - } - - SECTION("issue #93 reverse_iterator operator inheritance problem") - { - { - json a = {1, 2, 3}; - json::reverse_iterator rit = a.rbegin(); - ++rit; - CHECK(*rit == json(2)); - CHECK(rit.value() == json(2)); - } - { - json a = {1, 2, 3}; - json::reverse_iterator rit = ++a.rbegin(); - } - { - json a = {1, 2, 3}; - json::reverse_iterator rit = a.rbegin(); - ++rit; - json b = {0, 0, 0}; - std::transform(rit, a.rend(), b.rbegin(), [](json el) - { - return el; - }); - CHECK(b == json({0, 1, 2})); - } - { - json a = {1, 2, 3}; - json b = {0, 0, 0}; - std::transform(++a.rbegin(), a.rend(), b.rbegin(), [](json el) - { - return el; - }); - CHECK(b == json({0, 1, 2})); - } - } - - SECTION("issue #100 - failed to iterator json object with reverse_iterator") - { - json config = - { - { "111", 111 }, - { "112", 112 }, - { "113", 113 } - }; - - std::stringstream ss; - - for (auto it = config.begin(); it != config.end(); ++it) - { - ss << it.key() << ": " << it.value() << '\n'; - } - - for (auto it = config.rbegin(); it != config.rend(); ++it) - { - ss << it.key() << ": " << it.value() << '\n'; - } - - CHECK(ss.str() == "111: 111\n112: 112\n113: 113\n113: 113\n112: 112\n111: 111\n"); - } - - SECTION("issue #101 - binary string causes numbers to be dumped as hex") - { - int64_t number = 10; - std::string bytes{"\x00" "asdf\n", 6}; - json j; - j["int64"] = number; - j["binary string"] = bytes; - // make sure the number is really printed as decimal "10" and not as - // hexadecimal "a" - CHECK(j.dump() == "{\"binary string\":\"\\u0000asdf\\n\",\"int64\":10}"); - } - - SECTION("issue #111 - subsequent unicode chars") - { - std::string bytes{0x7, 0x7}; - json j; - j["string"] = bytes; - CHECK(j["string"] == "\u0007\u0007"); - } - - SECTION("issue #144 - implicit assignment to std::string fails") - { - json o = {{"name", "value"}}; - - std::string s1 = o["name"]; - CHECK(s1 == "value"); - - std::string s2; - s2 = o["name"]; - - CHECK(s2 == "value"); - } - - SECTION("issue #146 - character following a surrogate pair is skipped") - { - CHECK(json::parse("\"\\ud80c\\udc60abc\"").get() == u8"\U00013060abc"); - } - - SECTION("issue #171 - Cannot index by key of type static constexpr const char*") - { - json j; - - // Non-const access with key as "char []" - char array_key[] = "Key1"; - CHECK_NOTHROW(j[array_key] = 1); - CHECK(j[array_key] == json(1)); - - // Non-const access with key as "const char[]" - const char const_array_key[] = "Key2"; - CHECK_NOTHROW(j[const_array_key] = 2); - CHECK(j[const_array_key] == json(2)); - - // Non-const access with key as "char *" - char _ptr_key[] = "Key3"; - char* ptr_key = &_ptr_key[0]; - CHECK_NOTHROW(j[ptr_key] = 3); - CHECK(j[ptr_key] == json(3)); - - // Non-const access with key as "const char *" - const char* const_ptr_key = "Key4"; - CHECK_NOTHROW(j[const_ptr_key] = 4); - CHECK(j[const_ptr_key] == json(4)); - - // Non-const access with key as "static constexpr const char *" - static constexpr const char* constexpr_ptr_key = "Key5"; - CHECK_NOTHROW(j[constexpr_ptr_key] = 5); - CHECK(j[constexpr_ptr_key] == json(5)); - - const json j_const = j; - - // Const access with key as "char []" - CHECK(j_const[array_key] == json(1)); - - // Const access with key as "const char[]" - CHECK(j_const[const_array_key] == json(2)); - - // Const access with key as "char *" - CHECK(j_const[ptr_key] == json(3)); - - // Const access with key as "const char *" - CHECK(j_const[const_ptr_key] == json(4)); - - // Const access with key as "static constexpr const char *" - CHECK(j_const[constexpr_ptr_key] == json(5)); - } - - SECTION("issue #186 miloyip/nativejson-benchmark: floating-point parsing") - { - json j; - - j = json::parse("-0.0"); - CHECK(j.get() == -0.0); - - j = json::parse("2.22507385850720113605740979670913197593481954635164564e-308"); - CHECK(j.get() == 2.2250738585072009e-308); - - j = json::parse("0.999999999999999944488848768742172978818416595458984374"); - CHECK(j.get() == 0.99999999999999989); - - j = json::parse("1.00000000000000011102230246251565404236316680908203126"); - CHECK(j.get() == 1.00000000000000022); - - j = json::parse("7205759403792793199999e-5"); - CHECK(j.get() == 72057594037927928.0); - - j = json::parse("922337203685477529599999e-5"); - CHECK(j.get() == 9223372036854774784.0); - - j = json::parse("1014120480182583464902367222169599999e-5"); - CHECK(j.get() == 10141204801825834086073718800384.0); - - j = json::parse("5708990770823839207320493820740630171355185151999e-3"); - CHECK(j.get() == 5708990770823838890407843763683279797179383808.0); - - // create JSON class with nonstandard float number type - - // float - nlohmann::basic_json j_float = - 1.23e25f; - CHECK(j_float.get() == 1.23e25f); - - // double - nlohmann::basic_json j_double = - 1.23e35f; - CHECK(j_double.get() == 1.23e35f); - - // long double - nlohmann::basic_json - j_long_double = 1.23e45L; - CHECK(j_long_double.get() == 1.23e45L); - } - - SECTION("issue #228 - double values are serialized with commas as decimal points") - { - json j1a = 23.42; - json j1b = json::parse("23.42"); - - json j2a = 2342e-2; - //issue #230 - //json j2b = json::parse("2342e-2"); - - json j3a = 10E3; - json j3b = json::parse("10E3"); - json j3c = json::parse("10e3"); - - // class to create a locale that would use a comma for decimals - class CommaDecimalSeparator : public std::numpunct - { - protected: - char do_decimal_point() const - { - return ','; - } - }; - - // change locale to mess with decimal points - std::locale::global(std::locale(std::locale(), new CommaDecimalSeparator)); - - CHECK(j1a.dump() == "23.42"); - CHECK(j1b.dump() == "23.42"); - - // check if locale is properly reset - std::stringstream ss; - ss.imbue(std::locale(std::locale(), new CommaDecimalSeparator)); - ss << 47.11; - CHECK(ss.str() == "47,11"); - ss << j1a; - CHECK(ss.str() == "47,1123.42"); - ss << 47.11; - CHECK(ss.str() == "47,1123.4247,11"); - - CHECK(j2a.dump() == "23.42"); - //issue #230 - //CHECK(j2b.dump() == "23.42"); - - CHECK(j3a.dump() == "10000"); - CHECK(j3b.dump() == "10000"); - CHECK(j3c.dump() == "10000"); - //CHECK(j3b.dump() == "1E04"); // roundtrip error - //CHECK(j3c.dump() == "1e04"); // roundtrip error - } - - SECTION("issue #233 - Can't use basic_json::iterator as a base iterator for std::move_iterator") - { - json source = {"a", "b", "c"}; - json expected = {"a", "b"}; - json dest; - - std::copy_n(std::make_move_iterator(source.begin()), 2, std::back_inserter(dest)); - - CHECK(dest == expected); - } - - SECTION("issue #235 - ambiguous overload for 'push_back' and 'operator+='") - { - json data = {{"key", "value"}}; - data.push_back({"key2", "value2"}); - data += {"key3", "value3"}; - - CHECK(data == json({{"key", "value"}, {"key2", "value2"}, {"key3", "value3"}})); - } - - SECTION("issue #269 - diff generates incorrect patch when removing multiple array elements") - { - json doc = R"( { "arr1": [1, 2, 3, 4] } )"_json; - json expected = R"( { "arr1": [1, 2] } )"_json; - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("issue #283 - value() does not work with _json_pointer types") - { - json j = - { - {"object", {{"key1", 1}, {"key2", 2}}}, - }; - - int at_integer = j.at("/object/key2"_json_pointer); - int val_integer = j.value("/object/key2"_json_pointer, 0); - - CHECK(at_integer == val_integer); - } -} - -// special test case to check if memory is leaked if constructor throws - -template -struct my_allocator : std::allocator -{ - template - void construct(T*, Args&& ...) - { - throw std::bad_alloc(); - } -}; - -TEST_CASE("bad_alloc") -{ - SECTION("bad_alloc") - { - // create JSON type using the throwing allocator - using my_json = nlohmann::basic_json; - - // creating an object should throw - CHECK_THROWS_AS(my_json j(my_json::value_t::object), std::bad_alloc); - } -} From 91b6e223d91ece4c305448c4cef33d29a0f35827 Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 22:04:55 +0200 Subject: [PATCH 07/46] adjusted capacity test cases --- test/src/unit-capacity.cpp | 40 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/test/src/unit-capacity.cpp b/test/src/unit-capacity.cpp index 95100da9..f671ae99 100644 --- a/test/src/unit-capacity.cpp +++ b/test/src/unit-capacity.cpp @@ -48,8 +48,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } @@ -66,8 +66,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } @@ -86,8 +86,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() == j.end()); - CHECK(j_const.begin() == j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } @@ -104,8 +104,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } } @@ -125,8 +125,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() == j.end()); - CHECK(j_const.begin() == j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } @@ -143,8 +143,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } } @@ -162,8 +162,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } @@ -180,8 +180,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } @@ -198,8 +198,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } @@ -216,8 +216,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() == j.end()); - CHECK(j_const.begin() == j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } } From ff592c6d50f1b49e3c4c38dac014bf2b5b65fb1d Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 22:05:05 +0200 Subject: [PATCH 08/46] adjusted warning flags --- test/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Makefile b/test/Makefile index c56a2ae9..9edda2d2 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,7 +3,7 @@ ########################################################################## # additional flags -CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wno-keyword-macro -Wno-float-equal +CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wno-ctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wno-keyword-macro -Wno-float-equal CPPFLAGS += -I ../src -I . SOURCES = src/unit.cpp \ From bd75ef9f271da4093f8e409329464811673b3de4 Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 22:05:16 +0200 Subject: [PATCH 09/46] added check-fast flag --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index b6116cc4..f08d5cf3 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,9 @@ json_unit: check: json_unit test/json_unit "*" +check-fast: json_unit + test/json_unit + ########################################################################## # documentation tests From 2d3374c8b2661c83e457f3b6511c3b6f8d386cee Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 22:10:30 +0200 Subject: [PATCH 10/46] removed codecov --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 06f25879..b0cde48d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,8 +25,7 @@ matrix: - touch src/json.hpp - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - test/json_unit "*" - - coveralls --exclude test/src/catch.hpp --exclude test/src/unit.cpp --include src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' - - bash <(curl -s https://codecov.io/bash) + - coveralls --exclude test/src/catch.hpp --exclude test/src/unit*.cpp --include src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' env: COMPILER=g++-4.9 - os: linux From 38f562af2a707c197732b73005bbcb47c993fbbe Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 22:51:08 +0200 Subject: [PATCH 11/46] reorganization into smaller test units --- .travis.yml | 2 +- test/CMakeLists.txt | 9 +- test/Makefile | 9 +- ...-constructor.cpp => unit-constructor1.cpp} | 159 --- test/src/unit-constructor2.cpp | 191 ++++ test/src/unit-element_access1.cpp | 947 ++++++++++++++++++ ...nt_access.cpp => unit-element_access2.cpp} | 914 +---------------- ...unit-iterators.cpp => unit-iterators1.cpp} | 840 +--------------- test/src/unit-iterators2.cpp | 873 ++++++++++++++++ 9 files changed, 2026 insertions(+), 1918 deletions(-) rename test/src/{unit-constructor.cpp => unit-constructor1.cpp} (93%) create mode 100644 test/src/unit-constructor2.cpp create mode 100644 test/src/unit-element_access1.cpp rename test/src/{unit-element_access.cpp => unit-element_access2.cpp} (56%) rename test/src/{unit-iterators.cpp => unit-iterators1.cpp} (55%) create mode 100644 test/src/unit-iterators2.cpp diff --git a/.travis.yml b/.travis.yml index b0cde48d..1ab50bfa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ matrix: - touch src/json.hpp - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - test/json_unit "*" - - coveralls --exclude test/src/catch.hpp --exclude test/src/unit*.cpp --include src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' + - coveralls --exclude test/src/catch.hpp --exclude test/src/unit-algorithms.cpp --exclude test/src/unit-allocator.cpp --exclude test/src/unit-capacity.cpp --exclude test/src/unit-class_const_iterator.cpp --exclude test/src/unit-class_iterator.cpp --exclude test/src/unit-class_lexer.cpp --exclude test/src/unit-class_parser.cpp --exclude test/src/unit-comparison.cpp --exclude test/src/unit-concepts.cpp --exclude test/src/unit-constructor1.cpp --exclude test/src/unit-constructor2.cpp --exclude test/src/unit-convenience.cpp --exclude test/src/unit-conversions.cpp --exclude test/src/unit-deserialization.cpp --exclude test/src/unit-element_access1.cpp --exclude test/src/unit-element_access2.cpp --exclude test/src/unit-inspection.cpp --exclude test/src/unit-iterator_wrapper.cpp --exclude test/src/unit-iterators1.cpp --exclude test/src/unit-iterators2.cpp --exclude test/src/unit-json_patch.cpp --exclude test/src/unit-json_pointer.cpp --exclude test/src/unit-modifiers.cpp --exclude test/src/unit-pointer_access.cpp --exclude test/src/unit-readme.cpp --exclude test/src/unit-reference_access.cpp --exclude test/src/unit-regression.cpp --exclude test/src/unit-serialization.cpp --exclude test/src/unit-testsuites.cpp --exclude test/src/unit-unicode.cpp --include src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' env: COMPILER=g++-4.9 - os: linux diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 696a7f3f..782d5b53 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,14 +12,17 @@ add_executable(${JSON_UNITTEST_TARGET_NAME} "src/unit-class_parser.cpp" "src/unit-comparison.cpp" "src/unit-concepts.cpp" - "src/unit-constructor.cpp" + "src/unit-constructor1.cpp" + "src/unit-constructor2.cpp" "src/unit-convenience.cpp" "src/unit-conversions.cpp" "src/unit-deserialization.cpp" - "src/unit-element_access.cpp" + "src/unit-element_access1.cpp" + "src/unit-element_access2.cpp" "src/unit-inspection.cpp" "src/unit-iterator_wrapper.cpp" - "src/unit-iterators.cpp" + "src/unit-iterators1.cpp" + "src/unit-iterators2.cpp" "src/unit-json_patch.cpp" "src/unit-json_pointer.cpp" "src/unit-modifiers.cpp" diff --git a/test/Makefile b/test/Makefile index 9edda2d2..7bc8274f 100644 --- a/test/Makefile +++ b/test/Makefile @@ -16,14 +16,17 @@ SOURCES = src/unit.cpp \ src/unit-class_parser.cpp \ src/unit-comparison.cpp \ src/unit-concepts.cpp \ - src/unit-constructor.cpp \ + src/unit-constructor1.cpp \ + src/unit-constructor2.cpp \ src/unit-convenience.cpp \ src/unit-conversions.cpp \ src/unit-deserialization.cpp \ - src/unit-element_access.cpp \ + src/unit-element_access1.cpp \ + src/unit-element_access2.cpp \ src/unit-inspection.cpp \ src/unit-iterator_wrapper.cpp \ - src/unit-iterators.cpp \ + src/unit-iterators1.cpp \ + src/unit-iterators2.cpp \ src/unit-json_patch.cpp \ src/unit-json_pointer.cpp \ src/unit-modifiers.cpp \ diff --git a/test/src/unit-constructor.cpp b/test/src/unit-constructor1.cpp similarity index 93% rename from test/src/unit-constructor.cpp rename to test/src/unit-constructor1.cpp index e4842a58..87728648 100644 --- a/test/src/unit-constructor.cpp +++ b/test/src/unit-constructor1.cpp @@ -1309,162 +1309,3 @@ TEST_CASE("constructors") } } } - -TEST_CASE("other constructors and destructor") -{ - SECTION("copy constructor") - { - SECTION("object") - { - json j {{"foo", 1}, {"bar", false}}; - json k(j); - CHECK(j == k); - } - - SECTION("array") - { - json j {"foo", 1, 42.23, false}; - json k(j); - CHECK(j == k); - } - - SECTION("null") - { - json j(nullptr); - json k(j); - CHECK(j == k); - } - - SECTION("boolean") - { - json j(true); - json k(j); - CHECK(j == k); - } - - SECTION("string") - { - json j("Hello world"); - json k(j); - CHECK(j == k); - } - - SECTION("number (integer)") - { - json j(42); - json k(j); - CHECK(j == k); - } - - SECTION("number (unsigned)") - { - json j(42u); - json k(j); - CHECK(j == k); - } - - SECTION("number (floating-point)") - { - json j(42.23); - json k(j); - CHECK(j == k); - } - } - - SECTION("move constructor") - { - json j {{"foo", "bar"}, {"baz", {1, 2, 3, 4}}, {"a", 42u}, {"b", 42.23}, {"c", nullptr}}; - CHECK(j.type() == json::value_t::object); - json k(std::move(j)); - CHECK(k.type() == json::value_t::object); - CHECK(j.type() == json::value_t::null); - } - - SECTION("copy assignment") - { - SECTION("object") - { - json j {{"foo", 1}, {"bar", false}}; - json k; - k = j; - CHECK(j == k); - } - - SECTION("array") - { - json j {"foo", 1, 42.23, false}; - json k; - k = j; - CHECK(j == k); - } - - SECTION("null") - { - json j(nullptr); - json k; - k = j; - CHECK(j == k); - } - - SECTION("boolean") - { - json j(true); - json k; - k = j; - CHECK(j == k); - } - - SECTION("string") - { - json j("Hello world"); - json k; - k = j; - CHECK(j == k); - } - - SECTION("number (integer)") - { - json j(42); - json k; - k = j; - CHECK(j == k); - } - - SECTION("number (unsigned)") - { - json j(42u); - json k; - k = j; - CHECK(j == k); - } - - SECTION("number (floating-point)") - { - json j(42.23); - json k; - k = j; - CHECK(j == k); - } - } - - SECTION("destructor") - { - SECTION("object") - { - auto j = new json {{"foo", 1}, {"bar", false}}; - delete j; - } - - SECTION("array") - { - auto j = new json {"foo", 1, 1u, false, 23.42}; - delete j; - } - - SECTION("string") - { - auto j = new json("Hello world"); - delete j; - } - } -} diff --git a/test/src/unit-constructor2.cpp b/test/src/unit-constructor2.cpp new file mode 100644 index 00000000..b5f1a5e3 --- /dev/null +++ b/test/src/unit-constructor2.cpp @@ -0,0 +1,191 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("other constructors and destructor") +{ + SECTION("copy constructor") + { + SECTION("object") + { + json j {{"foo", 1}, {"bar", false}}; + json k(j); + CHECK(j == k); + } + + SECTION("array") + { + json j {"foo", 1, 42.23, false}; + json k(j); + CHECK(j == k); + } + + SECTION("null") + { + json j(nullptr); + json k(j); + CHECK(j == k); + } + + SECTION("boolean") + { + json j(true); + json k(j); + CHECK(j == k); + } + + SECTION("string") + { + json j("Hello world"); + json k(j); + CHECK(j == k); + } + + SECTION("number (integer)") + { + json j(42); + json k(j); + CHECK(j == k); + } + + SECTION("number (unsigned)") + { + json j(42u); + json k(j); + CHECK(j == k); + } + + SECTION("number (floating-point)") + { + json j(42.23); + json k(j); + CHECK(j == k); + } + } + + SECTION("move constructor") + { + json j {{"foo", "bar"}, {"baz", {1, 2, 3, 4}}, {"a", 42u}, {"b", 42.23}, {"c", nullptr}}; + CHECK(j.type() == json::value_t::object); + json k(std::move(j)); + CHECK(k.type() == json::value_t::object); + CHECK(j.type() == json::value_t::null); + } + + SECTION("copy assignment") + { + SECTION("object") + { + json j {{"foo", 1}, {"bar", false}}; + json k; + k = j; + CHECK(j == k); + } + + SECTION("array") + { + json j {"foo", 1, 42.23, false}; + json k; + k = j; + CHECK(j == k); + } + + SECTION("null") + { + json j(nullptr); + json k; + k = j; + CHECK(j == k); + } + + SECTION("boolean") + { + json j(true); + json k; + k = j; + CHECK(j == k); + } + + SECTION("string") + { + json j("Hello world"); + json k; + k = j; + CHECK(j == k); + } + + SECTION("number (integer)") + { + json j(42); + json k; + k = j; + CHECK(j == k); + } + + SECTION("number (unsigned)") + { + json j(42u); + json k; + k = j; + CHECK(j == k); + } + + SECTION("number (floating-point)") + { + json j(42.23); + json k; + k = j; + CHECK(j == k); + } + } + + SECTION("destructor") + { + SECTION("object") + { + auto j = new json {{"foo", 1}, {"bar", false}}; + delete j; + } + + SECTION("array") + { + auto j = new json {"foo", 1, 1u, false, 23.42}; + delete j; + } + + SECTION("string") + { + auto j = new json("Hello world"); + delete j; + } + } +} diff --git a/test/src/unit-element_access1.cpp b/test/src/unit-element_access1.cpp new file mode 100644 index 00000000..0e515a8e --- /dev/null +++ b/test/src/unit-element_access1.cpp @@ -0,0 +1,947 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("element access 1") +{ + SECTION("array") + { + json j = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + const json j_const = j; + + SECTION("access specified element with bounds checking") + { + SECTION("access within bounds") + { + CHECK(j.at(0) == json(1)); + CHECK(j.at(1) == json(1u)); + CHECK(j.at(2) == json(true)); + CHECK(j.at(3) == json(nullptr)); + CHECK(j.at(4) == json("string")); + CHECK(j.at(5) == json(42.23)); + CHECK(j.at(6) == json(json::object())); + CHECK(j.at(7) == json({1, 2, 3})); + + CHECK(j_const.at(0) == json(1)); + CHECK(j_const.at(1) == json(1u)); + CHECK(j_const.at(2) == json(true)); + CHECK(j_const.at(3) == json(nullptr)); + CHECK(j_const.at(4) == json("string")); + CHECK(j_const.at(5) == json(42.23)); + CHECK(j_const.at(6) == json(json::object())); + CHECK(j_const.at(7) == json({1, 2, 3})); + } + + SECTION("access outside bounds") + { + CHECK_THROWS_AS(j.at(8), std::out_of_range); + CHECK_THROWS_AS(j_const.at(8), std::out_of_range); + + CHECK_THROWS_WITH(j.at(8), "array index 8 is out of range"); + CHECK_THROWS_WITH(j_const.at(8), "array index 8 is out of range"); + } + + SECTION("access on non-array type") + { + SECTION("null") + { + json j_nonarray(json::value_t::null); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with null"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with null"); + } + + SECTION("boolean") + { + json j_nonarray(json::value_t::boolean); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with boolean"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with boolean"); + } + + SECTION("string") + { + json j_nonarray(json::value_t::string); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with string"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with string"); + } + + SECTION("object") + { + json j_nonarray(json::value_t::object); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with object"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with object"); + } + + SECTION("number (integer)") + { + json j_nonarray(json::value_t::number_integer); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); + } + + SECTION("number (unsigned)") + { + json j_nonarray(json::value_t::number_unsigned); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); + } + + SECTION("number (floating-point)") + { + json j_nonarray(json::value_t::number_float); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); + } + } + } + + SECTION("front and back") + { + CHECK(j.front() == json(1)); + CHECK(j_const.front() == json(1)); + CHECK(j.back() == json({1, 2, 3})); + CHECK(j_const.back() == json({1, 2, 3})); + } + + SECTION("access specified element") + { + SECTION("access within bounds") + { + CHECK(j[0] == json(1)); + CHECK(j[1] == json(1u)); + CHECK(j[2] == json(true)); + CHECK(j[3] == json(nullptr)); + CHECK(j[4] == json("string")); + CHECK(j[5] == json(42.23)); + CHECK(j[6] == json(json::object())); + CHECK(j[7] == json({1, 2, 3})); + + CHECK(j_const[0] == json(1)); + CHECK(j_const[1] == json(1u)); + CHECK(j_const[2] == json(true)); + CHECK(j_const[3] == json(nullptr)); + CHECK(j_const[4] == json("string")); + CHECK(j_const[5] == json(42.23)); + CHECK(j_const[6] == json(json::object())); + CHECK(j_const[7] == json({1, 2, 3})); + } + + SECTION("access on non-array type") + { + SECTION("null") + { + SECTION("standard tests") + { + json j_nonarray(json::value_t::null); + const json j_nonarray_const(j_nonarray); + CHECK_NOTHROW(j_nonarray[0]); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with null"); + } + + SECTION("implicit transformation to properly filled array") + { + json j_nonarray; + j_nonarray[3] = 42; + CHECK(j_nonarray == json({nullptr, nullptr, nullptr, 42})); + } + } + + SECTION("boolean") + { + json j_nonarray(json::value_t::boolean); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with boolean"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with boolean"); + } + + SECTION("string") + { + json j_nonarray(json::value_t::string); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with string"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with string"); + } + + SECTION("object") + { + json j_nonarray(json::value_t::object); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with object"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with object"); + } + + SECTION("number (integer)") + { + json j_nonarray(json::value_t::number_integer); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); + } + + SECTION("number (unsigned)") + { + json j_nonarray(json::value_t::number_unsigned); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); + } + + SECTION("number (floating-point)") + { + json j_nonarray(json::value_t::number_float); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); + } + } + } + + SECTION("remove specified element") + { + SECTION("remove element by index") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(0); + CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(1); + CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(2); + CHECK(jarray == json({1, 1u, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(3); + CHECK(jarray == json({1, 1u, true, "string", 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(4); + CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(5); + CHECK(jarray == json({1, 1u, true, nullptr, "string", json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(6); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(7); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object()})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + CHECK_THROWS_AS(jarray.erase(8), std::out_of_range); + CHECK_THROWS_WITH(jarray.erase(8), "array index 8 is out of range"); + } + } + + SECTION("remove element by iterator") + { + SECTION("erase(begin())") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it2 = jarray.erase(jarray.begin()); + CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(1u)); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it2 = jarray.erase(jarray.cbegin()); + CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(1u)); + } + } + + SECTION("erase(begin(), end())") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it2 = jarray.erase(jarray.begin(), jarray.end()); + CHECK(jarray == json::array()); + CHECK(it2 == jarray.end()); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cend()); + CHECK(jarray == json::array()); + CHECK(it2 == jarray.cend()); + } + } + + SECTION("erase(begin(), begin())") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it2 = jarray.erase(jarray.begin(), jarray.begin()); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(1)); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cbegin()); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(1)); + } + } + + SECTION("erase at offset") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it = jarray.begin() + 4; + json::iterator it2 = jarray.erase(it); + CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(42.23)); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it = jarray.cbegin() + 4; + json::const_iterator it2 = jarray.erase(it); + CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(42.23)); + } + } + + SECTION("erase subrange") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it2 = jarray.erase(jarray.begin() + 3, jarray.begin() + 6); + CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); + CHECK(*it2 == json::object()); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it2 = jarray.erase(jarray.cbegin() + 3, jarray.cbegin() + 6); + CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); + CHECK(*it2 == json::object()); + } + } + + SECTION("different arrays") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray2 = {"foo", "bar"}; + CHECK_THROWS_AS(jarray.erase(jarray2.begin()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray.end()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray2.end()), std::domain_error); + + CHECK_THROWS_WITH(jarray.erase(jarray2.begin()), "iterator does not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray.begin(), jarray2.end()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray.end()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray2.end()), + "iterators do not fit current value"); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray2 = {"foo", "bar"}; + CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), std::domain_error); + + CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin()), "iterator does not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray.cbegin(), jarray2.cend()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray.cend()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray2.cend()), + "iterators do not fit current value"); + } + } + } + + SECTION("remove element by index in non-array type") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with null"); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with boolean"); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with string"); + } + + SECTION("object") + { + json j_nonobject(json::value_t::object); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with object"); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); + } + + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); + } + } + } + } + + SECTION("other values") + { + SECTION("front and back") + { + SECTION("null") + { + { + json j; + CHECK_THROWS_AS(j.front(), std::out_of_range); + CHECK_THROWS_AS(j.back(), std::out_of_range); + CHECK_THROWS_WITH(j.front(), "cannot get value"); + CHECK_THROWS_WITH(j.back(), "cannot get value"); + } + { + const json j{}; + CHECK_THROWS_AS(j.front(), std::out_of_range); + CHECK_THROWS_AS(j.back(), std::out_of_range); + CHECK_THROWS_WITH(j.front(), "cannot get value"); + CHECK_THROWS_WITH(j.back(), "cannot get value"); + } + } + + SECTION("string") + { + { + json j = "foo"; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = "bar"; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = true; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = 17; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = 17u; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = 23.42; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + } + + SECTION("erase with one valid iterator") + { + SECTION("null") + { + { + json j; + CHECK_THROWS_AS(j.erase(j.begin()), std::domain_error); + CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); + } + { + json j; + CHECK_THROWS_AS(j.erase(j.cbegin()), std::domain_error); + CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); + } + } + + SECTION("string") + { + { + json j = "foo"; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = "bar"; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = true; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 17; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 17u; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 23.42; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + } + + SECTION("erase with one invalid iterator") + { + SECTION("string") + { + { + json j = "foo"; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = "bar"; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = true; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = 17; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = 23.42; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + } + + SECTION("erase with two valid iterators") + { + SECTION("null") + { + { + json j; + CHECK_THROWS_AS(j.erase(j.begin(), j.end()), std::domain_error); + CHECK_THROWS_WITH(j.erase(j.begin(), j.end()), "cannot use erase() with null"); + } + { + json j; + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cend()), std::domain_error); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cend()), "cannot use erase() with null"); + } + } + + SECTION("string") + { + { + json j = "foo"; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = "bar"; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = true; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 17; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 17u; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 23.42; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + } + + SECTION("erase with two invalid iterators") + { + SECTION("string") + { + { + json j = "foo"; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = "bar"; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = true; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 17; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 23.42; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + } + } +} diff --git a/test/src/unit-element_access.cpp b/test/src/unit-element_access2.cpp similarity index 56% rename from test/src/unit-element_access.cpp rename to test/src/unit-element_access2.cpp index bd33e1e0..adadb726 100644 --- a/test/src/unit-element_access.cpp +++ b/test/src/unit-element_access2.cpp @@ -31,466 +31,8 @@ SOFTWARE. #include "json.hpp" using nlohmann::json; -TEST_CASE("element access") +TEST_CASE("element access 2") { - SECTION("array") - { - json j = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - const json j_const = j; - - SECTION("access specified element with bounds checking") - { - SECTION("access within bounds") - { - CHECK(j.at(0) == json(1)); - CHECK(j.at(1) == json(1u)); - CHECK(j.at(2) == json(true)); - CHECK(j.at(3) == json(nullptr)); - CHECK(j.at(4) == json("string")); - CHECK(j.at(5) == json(42.23)); - CHECK(j.at(6) == json(json::object())); - CHECK(j.at(7) == json({1, 2, 3})); - - CHECK(j_const.at(0) == json(1)); - CHECK(j_const.at(1) == json(1u)); - CHECK(j_const.at(2) == json(true)); - CHECK(j_const.at(3) == json(nullptr)); - CHECK(j_const.at(4) == json("string")); - CHECK(j_const.at(5) == json(42.23)); - CHECK(j_const.at(6) == json(json::object())); - CHECK(j_const.at(7) == json({1, 2, 3})); - } - - SECTION("access outside bounds") - { - CHECK_THROWS_AS(j.at(8), std::out_of_range); - CHECK_THROWS_AS(j_const.at(8), std::out_of_range); - - CHECK_THROWS_WITH(j.at(8), "array index 8 is out of range"); - CHECK_THROWS_WITH(j_const.at(8), "array index 8 is out of range"); - } - - SECTION("access on non-array type") - { - SECTION("null") - { - json j_nonarray(json::value_t::null); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with null"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with null"); - } - - SECTION("boolean") - { - json j_nonarray(json::value_t::boolean); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with boolean"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with boolean"); - } - - SECTION("string") - { - json j_nonarray(json::value_t::string); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with string"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with string"); - } - - SECTION("object") - { - json j_nonarray(json::value_t::object); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with object"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with object"); - } - - SECTION("number (integer)") - { - json j_nonarray(json::value_t::number_integer); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); - } - - SECTION("number (unsigned)") - { - json j_nonarray(json::value_t::number_unsigned); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); - } - - SECTION("number (floating-point)") - { - json j_nonarray(json::value_t::number_float); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); - } - } - } - - SECTION("front and back") - { - CHECK(j.front() == json(1)); - CHECK(j_const.front() == json(1)); - CHECK(j.back() == json({1, 2, 3})); - CHECK(j_const.back() == json({1, 2, 3})); - } - - SECTION("access specified element") - { - SECTION("access within bounds") - { - CHECK(j[0] == json(1)); - CHECK(j[1] == json(1u)); - CHECK(j[2] == json(true)); - CHECK(j[3] == json(nullptr)); - CHECK(j[4] == json("string")); - CHECK(j[5] == json(42.23)); - CHECK(j[6] == json(json::object())); - CHECK(j[7] == json({1, 2, 3})); - - CHECK(j_const[0] == json(1)); - CHECK(j_const[1] == json(1u)); - CHECK(j_const[2] == json(true)); - CHECK(j_const[3] == json(nullptr)); - CHECK(j_const[4] == json("string")); - CHECK(j_const[5] == json(42.23)); - CHECK(j_const[6] == json(json::object())); - CHECK(j_const[7] == json({1, 2, 3})); - } - - SECTION("access on non-array type") - { - SECTION("null") - { - SECTION("standard tests") - { - json j_nonarray(json::value_t::null); - const json j_nonarray_const(j_nonarray); - CHECK_NOTHROW(j_nonarray[0]); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with null"); - } - - SECTION("implicit transformation to properly filled array") - { - json j_nonarray; - j_nonarray[3] = 42; - CHECK(j_nonarray == json({nullptr, nullptr, nullptr, 42})); - } - } - - SECTION("boolean") - { - json j_nonarray(json::value_t::boolean); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with boolean"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with boolean"); - } - - SECTION("string") - { - json j_nonarray(json::value_t::string); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with string"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with string"); - } - - SECTION("object") - { - json j_nonarray(json::value_t::object); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with object"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with object"); - } - - SECTION("number (integer)") - { - json j_nonarray(json::value_t::number_integer); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); - } - - SECTION("number (unsigned)") - { - json j_nonarray(json::value_t::number_unsigned); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); - } - - SECTION("number (floating-point)") - { - json j_nonarray(json::value_t::number_float); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); - } - } - } - - SECTION("remove specified element") - { - SECTION("remove element by index") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(0); - CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(1); - CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(2); - CHECK(jarray == json({1, 1u, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(3); - CHECK(jarray == json({1, 1u, true, "string", 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(4); - CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(5); - CHECK(jarray == json({1, 1u, true, nullptr, "string", json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(6); - CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(7); - CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object()})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - CHECK_THROWS_AS(jarray.erase(8), std::out_of_range); - CHECK_THROWS_WITH(jarray.erase(8), "array index 8 is out of range"); - } - } - - SECTION("remove element by iterator") - { - SECTION("erase(begin())") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it2 = jarray.erase(jarray.begin()); - CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(1u)); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it2 = jarray.erase(jarray.cbegin()); - CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(1u)); - } - } - - SECTION("erase(begin(), end())") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it2 = jarray.erase(jarray.begin(), jarray.end()); - CHECK(jarray == json::array()); - CHECK(it2 == jarray.end()); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cend()); - CHECK(jarray == json::array()); - CHECK(it2 == jarray.cend()); - } - } - - SECTION("erase(begin(), begin())") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it2 = jarray.erase(jarray.begin(), jarray.begin()); - CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(1)); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cbegin()); - CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(1)); - } - } - - SECTION("erase at offset") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it = jarray.begin() + 4; - json::iterator it2 = jarray.erase(it); - CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(42.23)); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it = jarray.cbegin() + 4; - json::const_iterator it2 = jarray.erase(it); - CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(42.23)); - } - } - - SECTION("erase subrange") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it2 = jarray.erase(jarray.begin() + 3, jarray.begin() + 6); - CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); - CHECK(*it2 == json::object()); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it2 = jarray.erase(jarray.cbegin() + 3, jarray.cbegin() + 6); - CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); - CHECK(*it2 == json::object()); - } - } - - SECTION("different arrays") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json jarray2 = {"foo", "bar"}; - CHECK_THROWS_AS(jarray.erase(jarray2.begin()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray.end()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray2.end()), std::domain_error); - - CHECK_THROWS_WITH(jarray.erase(jarray2.begin()), "iterator does not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray.begin(), jarray2.end()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray.end()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray2.end()), - "iterators do not fit current value"); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json jarray2 = {"foo", "bar"}; - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), std::domain_error); - - CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin()), "iterator does not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray.cbegin(), jarray2.cend()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray.cend()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray2.cend()), - "iterators do not fit current value"); - } - } - } - - SECTION("remove element by index in non-array type") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with null"); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with boolean"); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with string"); - } - - SECTION("object") - { - json j_nonobject(json::value_t::object); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with object"); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); - } - - SECTION("number (unsigned)") - { - json j_nonobject(json::value_t::number_unsigned); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); - } - } - } - } - SECTION("object") { json j = {{"integer", 1}, {"unsigned", 1u}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", json::object()}, {"array", {1, 2, 3}}}; @@ -1414,458 +956,4 @@ TEST_CASE("element access") } } } - - SECTION("other values") - { - SECTION("front and back") - { - SECTION("null") - { - { - json j; - CHECK_THROWS_AS(j.front(), std::out_of_range); - CHECK_THROWS_AS(j.back(), std::out_of_range); - CHECK_THROWS_WITH(j.front(), "cannot get value"); - CHECK_THROWS_WITH(j.back(), "cannot get value"); - } - { - const json j{}; - CHECK_THROWS_AS(j.front(), std::out_of_range); - CHECK_THROWS_AS(j.back(), std::out_of_range); - CHECK_THROWS_WITH(j.front(), "cannot get value"); - CHECK_THROWS_WITH(j.back(), "cannot get value"); - } - } - - SECTION("string") - { - { - json j = "foo"; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = "bar"; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = true; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = 17; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = 17u; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = 23.42; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - } - - SECTION("erase with one valid iterator") - { - SECTION("null") - { - { - json j; - CHECK_THROWS_AS(j.erase(j.begin()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); - } - { - json j; - CHECK_THROWS_AS(j.erase(j.cbegin()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); - } - } - - SECTION("string") - { - { - json j = "foo"; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = "bar"; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = true; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 17; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 17u; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 23.42; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - } - - SECTION("erase with one invalid iterator") - { - SECTION("string") - { - { - json j = "foo"; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = "bar"; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = true; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = 17; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = 17u; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = 23.42; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - } - - SECTION("erase with two valid iterators") - { - SECTION("null") - { - { - json j; - CHECK_THROWS_AS(j.erase(j.begin(), j.end()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.begin(), j.end()), "cannot use erase() with null"); - } - { - json j; - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cend()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cend()), "cannot use erase() with null"); - } - } - - SECTION("string") - { - { - json j = "foo"; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = "bar"; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = true; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 17; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 17u; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 23.42; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - } - - SECTION("erase with two invalid iterators") - { - SECTION("string") - { - { - json j = "foo"; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = "bar"; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = true; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 17; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 17u; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 23.42; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - } - } } diff --git a/test/src/unit-iterators.cpp b/test/src/unit-iterators1.cpp similarity index 55% rename from test/src/unit-iterators.cpp rename to test/src/unit-iterators1.cpp index 06dcb6aa..f8b4e6bd 100644 --- a/test/src/unit-iterators.cpp +++ b/test/src/unit-iterators1.cpp @@ -32,7 +32,7 @@ SOFTWARE. #include "json.hpp" using nlohmann::json; -TEST_CASE("iterators") +TEST_CASE("iterators 1") { SECTION("basic behavior") { @@ -1511,842 +1511,4 @@ TEST_CASE("iterators") } } } - - SECTION("iterator comparisons") - { - json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; - - for (json& j : j_values) - { - auto it1 = j.begin(); - auto it2 = j.begin(); - auto it3 = j.begin(); - ++it2; - ++it3; - ++it3; - auto it1_c = j.cbegin(); - auto it2_c = j.cbegin(); - auto it3_c = j.cbegin(); - ++it2_c; - ++it3_c; - ++it3_c; - - // comparison: equal - { - CHECK(it1 == it1); - CHECK(not (it1 == it2)); - CHECK(not (it1 == it3)); - CHECK(not (it2 == it3)); - CHECK(it1_c == it1_c); - CHECK(not (it1_c == it2_c)); - CHECK(not (it1_c == it3_c)); - CHECK(not (it2_c == it3_c)); - } - - // comparison: not equal - { - // check definition - CHECK( (it1 != it1) == not(it1 == it1) ); - CHECK( (it1 != it2) == not(it1 == it2) ); - CHECK( (it1 != it3) == not(it1 == it3) ); - CHECK( (it2 != it3) == not(it2 == it3) ); - CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); - CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); - CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); - CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); - } - - // comparison: smaller - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 < it1, std::domain_error); - CHECK_THROWS_AS(it1 < it2, std::domain_error); - CHECK_THROWS_AS(it2 < it3, std::domain_error); - CHECK_THROWS_AS(it1 < it3, std::domain_error); - CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); - } - else - { - CHECK(not (it1 < it1)); - CHECK(it1 < it2); - CHECK(it1 < it3); - CHECK(it2 < it3); - CHECK(not (it1_c < it1_c)); - CHECK(it1_c < it2_c); - CHECK(it1_c < it3_c); - CHECK(it2_c < it3_c); - } - } - - // comparison: less than or equal - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 <= it1, std::domain_error); - CHECK_THROWS_AS(it1 <= it2, std::domain_error); - CHECK_THROWS_AS(it2 <= it3, std::domain_error); - CHECK_THROWS_AS(it1 <= it3, std::domain_error); - CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 <= it1) == not(it1 < it1) ); - CHECK( (it1 <= it2) == not(it2 < it1) ); - CHECK( (it1 <= it3) == not(it3 < it1) ); - CHECK( (it2 <= it3) == not(it3 < it2) ); - CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); - CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); - CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); - CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); - } - } - - // comparison: greater than - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 > it1, std::domain_error); - CHECK_THROWS_AS(it1 > it2, std::domain_error); - CHECK_THROWS_AS(it2 > it3, std::domain_error); - CHECK_THROWS_AS(it1 > it3, std::domain_error); - CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 > it1) == (it1 < it1) ); - CHECK( (it1 > it2) == (it2 < it1) ); - CHECK( (it1 > it3) == (it3 < it1) ); - CHECK( (it2 > it3) == (it3 < it2) ); - CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); - CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); - CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); - CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); - } - } - - // comparison: greater than or equal - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 >= it1, std::domain_error); - CHECK_THROWS_AS(it1 >= it2, std::domain_error); - CHECK_THROWS_AS(it2 >= it3, std::domain_error); - CHECK_THROWS_AS(it1 >= it3, std::domain_error); - CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 >= it1) == not(it1 < it1) ); - CHECK( (it1 >= it2) == not(it1 < it2) ); - CHECK( (it1 >= it3) == not(it1 < it3) ); - CHECK( (it2 >= it3) == not(it2 < it3) ); - CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); - CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); - CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); - CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); - } - } - } - - // check exceptions if different objects are compared - for (auto j : j_values) - { - for (auto k : j_values) - { - if (j != k) - { - CHECK_THROWS_AS(j.begin() == k.begin(), std::domain_error); - CHECK_THROWS_AS(j.cbegin() == k.cbegin(), std::domain_error); - CHECK_THROWS_WITH(j.begin() == k.begin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "cannot compare iterators of different containers"); - - CHECK_THROWS_AS(j.begin() < k.begin(), std::domain_error); - CHECK_THROWS_AS(j.cbegin() < k.cbegin(), std::domain_error); - CHECK_THROWS_WITH(j.begin() < k.begin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "cannot compare iterators of different containers"); - } - } - } - } - - SECTION("iterator arithmetic") - { - json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j_array = {1, 2, 3, 4, 5, 6}; - json j_null = nullptr; - json j_value = 42; - - SECTION("addition and subtraction") - { - SECTION("object") - { - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); - } - } - - SECTION("array") - { - { - auto it = j_array.begin(); - it += 3; - CHECK((j_array.begin() + 3) == it); - CHECK((it - 3) == j_array.begin()); - CHECK((it - j_array.begin()) == 3); - CHECK(*it == json(4)); - it -= 2; - CHECK(*it == json(2)); - } - { - auto it = j_array.cbegin(); - it += 3; - CHECK((j_array.cbegin() + 3) == it); - CHECK((it - 3) == j_array.cbegin()); - CHECK((it - j_array.cbegin()) == 3); - CHECK(*it == json(4)); - it -= 2; - CHECK(*it == json(2)); - } - } - - SECTION("null") - { - { - auto it = j_null.begin(); - it += 3; - CHECK((j_null.begin() + 3) == it); - CHECK((it - 3) == j_null.begin()); - CHECK((it - j_null.begin()) == 3); - CHECK(it != j_null.end()); - it -= 3; - CHECK(it == j_null.end()); - } - { - auto it = j_null.cbegin(); - it += 3; - CHECK((j_null.cbegin() + 3) == it); - CHECK((it - 3) == j_null.cbegin()); - CHECK((it - j_null.cbegin()) == 3); - CHECK(it != j_null.cend()); - it -= 3; - CHECK(it == j_null.cend()); - } - } - - SECTION("value") - { - { - auto it = j_value.begin(); - it += 3; - CHECK((j_value.begin() + 3) == it); - CHECK((it - 3) == j_value.begin()); - CHECK((it - j_value.begin()) == 3); - CHECK(it != j_value.end()); - it -= 3; - CHECK(*it == json(42)); - } - { - auto it = j_value.cbegin(); - it += 3; - CHECK((j_value.cbegin() + 3) == it); - CHECK((it - 3) == j_value.cbegin()); - CHECK((it - j_value.cbegin()) == 3); - CHECK(it != j_value.cend()); - it -= 3; - CHECK(*it == json(42)); - } - } - } - - SECTION("subscript operator") - { - SECTION("object") - { - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); - } - } - - SECTION("array") - { - { - auto it = j_array.begin(); - CHECK(it[0] == json(1)); - CHECK(it[1] == json(2)); - CHECK(it[2] == json(3)); - CHECK(it[3] == json(4)); - CHECK(it[4] == json(5)); - CHECK(it[5] == json(6)); - } - { - auto it = j_array.cbegin(); - CHECK(it[0] == json(1)); - CHECK(it[1] == json(2)); - CHECK(it[2] == json(3)); - CHECK(it[3] == json(4)); - CHECK(it[4] == json(5)); - CHECK(it[5] == json(6)); - } - } - - SECTION("null") - { - { - auto it = j_null.begin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - { - auto it = j_null.cbegin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - } - - SECTION("value") - { - { - auto it = j_value.begin(); - CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - { - auto it = j_value.cbegin(); - CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - } - } - } - - SECTION("reverse iterator comparisons") - { - json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; - - for (json& j : j_values) - { - auto it1 = j.rbegin(); - auto it2 = j.rbegin(); - auto it3 = j.rbegin(); - ++it2; - ++it3; - ++it3; - auto it1_c = j.crbegin(); - auto it2_c = j.crbegin(); - auto it3_c = j.crbegin(); - ++it2_c; - ++it3_c; - ++it3_c; - - // comparison: equal - { - CHECK(it1 == it1); - CHECK(not (it1 == it2)); - CHECK(not (it1 == it3)); - CHECK(not (it2 == it3)); - CHECK(it1_c == it1_c); - CHECK(not (it1_c == it2_c)); - CHECK(not (it1_c == it3_c)); - CHECK(not (it2_c == it3_c)); - } - - // comparison: not equal - { - // check definition - CHECK( (it1 != it1) == not(it1 == it1) ); - CHECK( (it1 != it2) == not(it1 == it2) ); - CHECK( (it1 != it3) == not(it1 == it3) ); - CHECK( (it2 != it3) == not(it2 == it3) ); - CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); - CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); - CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); - CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); - } - - // comparison: smaller - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 < it1, std::domain_error); - CHECK_THROWS_AS(it1 < it2, std::domain_error); - CHECK_THROWS_AS(it2 < it3, std::domain_error); - CHECK_THROWS_AS(it1 < it3, std::domain_error); - CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); - } - else - { - CHECK(not (it1 < it1)); - CHECK(it1 < it2); - CHECK(it1 < it3); - CHECK(it2 < it3); - CHECK(not (it1_c < it1_c)); - CHECK(it1_c < it2_c); - CHECK(it1_c < it3_c); - CHECK(it2_c < it3_c); - } - } - - // comparison: less than or equal - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 <= it1, std::domain_error); - CHECK_THROWS_AS(it1 <= it2, std::domain_error); - CHECK_THROWS_AS(it2 <= it3, std::domain_error); - CHECK_THROWS_AS(it1 <= it3, std::domain_error); - CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 <= it1) == not(it1 < it1) ); - CHECK( (it1 <= it2) == not(it2 < it1) ); - CHECK( (it1 <= it3) == not(it3 < it1) ); - CHECK( (it2 <= it3) == not(it3 < it2) ); - CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); - CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); - CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); - CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); - } - } - - // comparison: greater than - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 > it1, std::domain_error); - CHECK_THROWS_AS(it1 > it2, std::domain_error); - CHECK_THROWS_AS(it2 > it3, std::domain_error); - CHECK_THROWS_AS(it1 > it3, std::domain_error); - CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 > it1) == (it1 < it1) ); - CHECK( (it1 > it2) == (it2 < it1) ); - CHECK( (it1 > it3) == (it3 < it1) ); - CHECK( (it2 > it3) == (it3 < it2) ); - CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); - CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); - CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); - CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); - } - } - - // comparison: greater than or equal - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 >= it1, std::domain_error); - CHECK_THROWS_AS(it1 >= it2, std::domain_error); - CHECK_THROWS_AS(it2 >= it3, std::domain_error); - CHECK_THROWS_AS(it1 >= it3, std::domain_error); - CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 >= it1) == not(it1 < it1) ); - CHECK( (it1 >= it2) == not(it1 < it2) ); - CHECK( (it1 >= it3) == not(it1 < it3) ); - CHECK( (it2 >= it3) == not(it2 < it3) ); - CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); - CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); - CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); - CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); - } - } - } - - // check exceptions if different objects are compared - for (auto j : j_values) - { - for (auto k : j_values) - { - if (j != k) - { - CHECK_THROWS_AS(j.rbegin() == k.rbegin(), std::domain_error); - CHECK_THROWS_AS(j.crbegin() == k.crbegin(), std::domain_error); - CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "cannot compare iterators of different containers"); - - CHECK_THROWS_AS(j.rbegin() < k.rbegin(), std::domain_error); - CHECK_THROWS_AS(j.crbegin() < k.crbegin(), std::domain_error); - CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "cannot compare iterators of different containers"); - } - } - } - } - - SECTION("reverse iterator arithmetic") - { - json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j_array = {1, 2, 3, 4, 5, 6}; - json j_null = nullptr; - json j_value = 42; - - SECTION("addition and subtraction") - { - SECTION("object") - { - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); - } - } - - SECTION("array") - { - { - auto it = j_array.rbegin(); - it += 3; - CHECK((j_array.rbegin() + 3) == it); - CHECK((it - 3) == j_array.rbegin()); - CHECK((j_array.rbegin() - it) == 3); - CHECK(*it == json(3)); - it -= 2; - CHECK(*it == json(5)); - } - { - auto it = j_array.crbegin(); - it += 3; - CHECK((j_array.crbegin() + 3) == it); - CHECK((it - 3) == j_array.crbegin()); - CHECK((j_array.crbegin() - it) == 3); - CHECK(*it == json(3)); - it -= 2; - CHECK(*it == json(5)); - } - } - - SECTION("null") - { - { - auto it = j_null.rbegin(); - it += 3; - CHECK((j_null.rbegin() + 3) == it); - CHECK((it - 3) == j_null.rbegin()); - CHECK((j_null.rbegin() - it) == 3); - CHECK(it != j_null.rend()); - it -= 3; - CHECK(it == j_null.rend()); - } - { - auto it = j_null.crbegin(); - it += 3; - CHECK((j_null.crbegin() + 3) == it); - CHECK((it - 3) == j_null.crbegin()); - CHECK((j_null.crbegin() - it) == 3); - CHECK(it != j_null.crend()); - it -= 3; - CHECK(it == j_null.crend()); - } - } - - SECTION("value") - { - { - auto it = j_value.rbegin(); - it += 3; - CHECK((j_value.rbegin() + 3) == it); - CHECK((it - 3) == j_value.rbegin()); - CHECK((j_value.rbegin() - it) == 3); - CHECK(it != j_value.rend()); - it -= 3; - CHECK(*it == json(42)); - } - { - auto it = j_value.crbegin(); - it += 3; - CHECK((j_value.crbegin() + 3) == it); - CHECK((it - 3) == j_value.crbegin()); - CHECK((j_value.crbegin() - it) == 3); - CHECK(it != j_value.crend()); - it -= 3; - CHECK(*it == json(42)); - } - } - } - - SECTION("subscript operator") - { - SECTION("object") - { - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); - } - } - - SECTION("array") - { - { - auto it = j_array.rbegin(); - CHECK(it[0] == json(6)); - CHECK(it[1] == json(5)); - CHECK(it[2] == json(4)); - CHECK(it[3] == json(3)); - CHECK(it[4] == json(2)); - CHECK(it[5] == json(1)); - } - { - auto it = j_array.crbegin(); - CHECK(it[0] == json(6)); - CHECK(it[1] == json(5)); - CHECK(it[2] == json(4)); - CHECK(it[3] == json(3)); - CHECK(it[4] == json(2)); - CHECK(it[5] == json(1)); - } - } - - SECTION("null") - { - { - auto it = j_null.rbegin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - { - auto it = j_null.crbegin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - } - - SECTION("value") - { - { - auto it = j_value.rbegin(); - CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - { - auto it = j_value.crbegin(); - CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - } - } - } } diff --git a/test/src/unit-iterators2.cpp b/test/src/unit-iterators2.cpp new file mode 100644 index 00000000..cc5d9818 --- /dev/null +++ b/test/src/unit-iterators2.cpp @@ -0,0 +1,873 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("iterators 2") +{ + SECTION("iterator comparisons") + { + json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; + + for (json& j : j_values) + { + auto it1 = j.begin(); + auto it2 = j.begin(); + auto it3 = j.begin(); + ++it2; + ++it3; + ++it3; + auto it1_c = j.cbegin(); + auto it2_c = j.cbegin(); + auto it3_c = j.cbegin(); + ++it2_c; + ++it3_c; + ++it3_c; + + // comparison: equal + { + CHECK(it1 == it1); + CHECK(not (it1 == it2)); + CHECK(not (it1 == it3)); + CHECK(not (it2 == it3)); + CHECK(it1_c == it1_c); + CHECK(not (it1_c == it2_c)); + CHECK(not (it1_c == it3_c)); + CHECK(not (it2_c == it3_c)); + } + + // comparison: not equal + { + // check definition + CHECK( (it1 != it1) == not(it1 == it1) ); + CHECK( (it1 != it2) == not(it1 == it2) ); + CHECK( (it1 != it3) == not(it1 == it3) ); + CHECK( (it2 != it3) == not(it2 == it3) ); + CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); + CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); + CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); + CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); + } + + // comparison: smaller + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 < it1, std::domain_error); + CHECK_THROWS_AS(it1 < it2, std::domain_error); + CHECK_THROWS_AS(it2 < it3, std::domain_error); + CHECK_THROWS_AS(it1 < it3, std::domain_error); + CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); + } + else + { + CHECK(not (it1 < it1)); + CHECK(it1 < it2); + CHECK(it1 < it3); + CHECK(it2 < it3); + CHECK(not (it1_c < it1_c)); + CHECK(it1_c < it2_c); + CHECK(it1_c < it3_c); + CHECK(it2_c < it3_c); + } + } + + // comparison: less than or equal + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 <= it1, std::domain_error); + CHECK_THROWS_AS(it1 <= it2, std::domain_error); + CHECK_THROWS_AS(it2 <= it3, std::domain_error); + CHECK_THROWS_AS(it1 <= it3, std::domain_error); + CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 <= it1) == not(it1 < it1) ); + CHECK( (it1 <= it2) == not(it2 < it1) ); + CHECK( (it1 <= it3) == not(it3 < it1) ); + CHECK( (it2 <= it3) == not(it3 < it2) ); + CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); + CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); + CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); + CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); + } + } + + // comparison: greater than + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 > it1, std::domain_error); + CHECK_THROWS_AS(it1 > it2, std::domain_error); + CHECK_THROWS_AS(it2 > it3, std::domain_error); + CHECK_THROWS_AS(it1 > it3, std::domain_error); + CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 > it1) == (it1 < it1) ); + CHECK( (it1 > it2) == (it2 < it1) ); + CHECK( (it1 > it3) == (it3 < it1) ); + CHECK( (it2 > it3) == (it3 < it2) ); + CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); + CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); + CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); + CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); + } + } + + // comparison: greater than or equal + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 >= it1, std::domain_error); + CHECK_THROWS_AS(it1 >= it2, std::domain_error); + CHECK_THROWS_AS(it2 >= it3, std::domain_error); + CHECK_THROWS_AS(it1 >= it3, std::domain_error); + CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 >= it1) == not(it1 < it1) ); + CHECK( (it1 >= it2) == not(it1 < it2) ); + CHECK( (it1 >= it3) == not(it1 < it3) ); + CHECK( (it2 >= it3) == not(it2 < it3) ); + CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); + CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); + CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); + CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); + } + } + } + + // check exceptions if different objects are compared + for (auto j : j_values) + { + for (auto k : j_values) + { + if (j != k) + { + CHECK_THROWS_AS(j.begin() == k.begin(), std::domain_error); + CHECK_THROWS_AS(j.cbegin() == k.cbegin(), std::domain_error); + CHECK_THROWS_WITH(j.begin() == k.begin(), "cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "cannot compare iterators of different containers"); + + CHECK_THROWS_AS(j.begin() < k.begin(), std::domain_error); + CHECK_THROWS_AS(j.cbegin() < k.cbegin(), std::domain_error); + CHECK_THROWS_WITH(j.begin() < k.begin(), "cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "cannot compare iterators of different containers"); + } + } + } + } + + SECTION("iterator arithmetic") + { + json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j_array = {1, 2, 3, 4, 5, 6}; + json j_null = nullptr; + json j_value = 42; + + SECTION("addition and subtraction") + { + SECTION("object") + { + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it += 1, std::domain_error); + CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it += 1, std::domain_error); + CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it + 1, std::domain_error); + CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it + 1, std::domain_error); + CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it -= 1, std::domain_error); + CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it -= 1, std::domain_error); + CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it - 1, std::domain_error); + CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it - 1, std::domain_error); + CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it - it, std::domain_error); + CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it - it, std::domain_error); + CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + } + } + + SECTION("array") + { + { + auto it = j_array.begin(); + it += 3; + CHECK((j_array.begin() + 3) == it); + CHECK((it - 3) == j_array.begin()); + CHECK((it - j_array.begin()) == 3); + CHECK(*it == json(4)); + it -= 2; + CHECK(*it == json(2)); + } + { + auto it = j_array.cbegin(); + it += 3; + CHECK((j_array.cbegin() + 3) == it); + CHECK((it - 3) == j_array.cbegin()); + CHECK((it - j_array.cbegin()) == 3); + CHECK(*it == json(4)); + it -= 2; + CHECK(*it == json(2)); + } + } + + SECTION("null") + { + { + auto it = j_null.begin(); + it += 3; + CHECK((j_null.begin() + 3) == it); + CHECK((it - 3) == j_null.begin()); + CHECK((it - j_null.begin()) == 3); + CHECK(it != j_null.end()); + it -= 3; + CHECK(it == j_null.end()); + } + { + auto it = j_null.cbegin(); + it += 3; + CHECK((j_null.cbegin() + 3) == it); + CHECK((it - 3) == j_null.cbegin()); + CHECK((it - j_null.cbegin()) == 3); + CHECK(it != j_null.cend()); + it -= 3; + CHECK(it == j_null.cend()); + } + } + + SECTION("value") + { + { + auto it = j_value.begin(); + it += 3; + CHECK((j_value.begin() + 3) == it); + CHECK((it - 3) == j_value.begin()); + CHECK((it - j_value.begin()) == 3); + CHECK(it != j_value.end()); + it -= 3; + CHECK(*it == json(42)); + } + { + auto it = j_value.cbegin(); + it += 3; + CHECK((j_value.cbegin() + 3) == it); + CHECK((it - 3) == j_value.cbegin()); + CHECK((it - j_value.cbegin()) == 3); + CHECK(it != j_value.cend()); + it -= 3; + CHECK(*it == json(42)); + } + } + } + + SECTION("subscript operator") + { + SECTION("object") + { + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it[0], std::domain_error); + CHECK_THROWS_AS(it[1], std::domain_error); + CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); + CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it[0], std::domain_error); + CHECK_THROWS_AS(it[1], std::domain_error); + CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); + CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); + } + } + + SECTION("array") + { + { + auto it = j_array.begin(); + CHECK(it[0] == json(1)); + CHECK(it[1] == json(2)); + CHECK(it[2] == json(3)); + CHECK(it[3] == json(4)); + CHECK(it[4] == json(5)); + CHECK(it[5] == json(6)); + } + { + auto it = j_array.cbegin(); + CHECK(it[0] == json(1)); + CHECK(it[1] == json(2)); + CHECK(it[2] == json(3)); + CHECK(it[3] == json(4)); + CHECK(it[4] == json(5)); + CHECK(it[5] == json(6)); + } + } + + SECTION("null") + { + { + auto it = j_null.begin(); + CHECK_THROWS_AS(it[0], std::out_of_range); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[0], "cannot get value"); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + { + auto it = j_null.cbegin(); + CHECK_THROWS_AS(it[0], std::out_of_range); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[0], "cannot get value"); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + } + + SECTION("value") + { + { + auto it = j_value.begin(); + CHECK(it[0] == json(42)); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + { + auto it = j_value.cbegin(); + CHECK(it[0] == json(42)); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + } + } + } + + SECTION("reverse iterator comparisons") + { + json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; + + for (json& j : j_values) + { + auto it1 = j.rbegin(); + auto it2 = j.rbegin(); + auto it3 = j.rbegin(); + ++it2; + ++it3; + ++it3; + auto it1_c = j.crbegin(); + auto it2_c = j.crbegin(); + auto it3_c = j.crbegin(); + ++it2_c; + ++it3_c; + ++it3_c; + + // comparison: equal + { + CHECK(it1 == it1); + CHECK(not (it1 == it2)); + CHECK(not (it1 == it3)); + CHECK(not (it2 == it3)); + CHECK(it1_c == it1_c); + CHECK(not (it1_c == it2_c)); + CHECK(not (it1_c == it3_c)); + CHECK(not (it2_c == it3_c)); + } + + // comparison: not equal + { + // check definition + CHECK( (it1 != it1) == not(it1 == it1) ); + CHECK( (it1 != it2) == not(it1 == it2) ); + CHECK( (it1 != it3) == not(it1 == it3) ); + CHECK( (it2 != it3) == not(it2 == it3) ); + CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); + CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); + CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); + CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); + } + + // comparison: smaller + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 < it1, std::domain_error); + CHECK_THROWS_AS(it1 < it2, std::domain_error); + CHECK_THROWS_AS(it2 < it3, std::domain_error); + CHECK_THROWS_AS(it1 < it3, std::domain_error); + CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); + } + else + { + CHECK(not (it1 < it1)); + CHECK(it1 < it2); + CHECK(it1 < it3); + CHECK(it2 < it3); + CHECK(not (it1_c < it1_c)); + CHECK(it1_c < it2_c); + CHECK(it1_c < it3_c); + CHECK(it2_c < it3_c); + } + } + + // comparison: less than or equal + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 <= it1, std::domain_error); + CHECK_THROWS_AS(it1 <= it2, std::domain_error); + CHECK_THROWS_AS(it2 <= it3, std::domain_error); + CHECK_THROWS_AS(it1 <= it3, std::domain_error); + CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 <= it1) == not(it1 < it1) ); + CHECK( (it1 <= it2) == not(it2 < it1) ); + CHECK( (it1 <= it3) == not(it3 < it1) ); + CHECK( (it2 <= it3) == not(it3 < it2) ); + CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); + CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); + CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); + CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); + } + } + + // comparison: greater than + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 > it1, std::domain_error); + CHECK_THROWS_AS(it1 > it2, std::domain_error); + CHECK_THROWS_AS(it2 > it3, std::domain_error); + CHECK_THROWS_AS(it1 > it3, std::domain_error); + CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 > it1) == (it1 < it1) ); + CHECK( (it1 > it2) == (it2 < it1) ); + CHECK( (it1 > it3) == (it3 < it1) ); + CHECK( (it2 > it3) == (it3 < it2) ); + CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); + CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); + CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); + CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); + } + } + + // comparison: greater than or equal + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 >= it1, std::domain_error); + CHECK_THROWS_AS(it1 >= it2, std::domain_error); + CHECK_THROWS_AS(it2 >= it3, std::domain_error); + CHECK_THROWS_AS(it1 >= it3, std::domain_error); + CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 >= it1) == not(it1 < it1) ); + CHECK( (it1 >= it2) == not(it1 < it2) ); + CHECK( (it1 >= it3) == not(it1 < it3) ); + CHECK( (it2 >= it3) == not(it2 < it3) ); + CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); + CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); + CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); + CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); + } + } + } + + // check exceptions if different objects are compared + for (auto j : j_values) + { + for (auto k : j_values) + { + if (j != k) + { + CHECK_THROWS_AS(j.rbegin() == k.rbegin(), std::domain_error); + CHECK_THROWS_AS(j.crbegin() == k.crbegin(), std::domain_error); + CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "cannot compare iterators of different containers"); + + CHECK_THROWS_AS(j.rbegin() < k.rbegin(), std::domain_error); + CHECK_THROWS_AS(j.crbegin() < k.crbegin(), std::domain_error); + CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "cannot compare iterators of different containers"); + } + } + } + } + + SECTION("reverse iterator arithmetic") + { + json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j_array = {1, 2, 3, 4, 5, 6}; + json j_null = nullptr; + json j_value = 42; + + SECTION("addition and subtraction") + { + SECTION("object") + { + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it += 1, std::domain_error); + CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it += 1, std::domain_error); + CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it + 1, std::domain_error); + CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it + 1, std::domain_error); + CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it -= 1, std::domain_error); + CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it -= 1, std::domain_error); + CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it - 1, std::domain_error); + CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it - 1, std::domain_error); + CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it - it, std::domain_error); + CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it - it, std::domain_error); + CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + } + } + + SECTION("array") + { + { + auto it = j_array.rbegin(); + it += 3; + CHECK((j_array.rbegin() + 3) == it); + CHECK((it - 3) == j_array.rbegin()); + CHECK((j_array.rbegin() - it) == 3); + CHECK(*it == json(3)); + it -= 2; + CHECK(*it == json(5)); + } + { + auto it = j_array.crbegin(); + it += 3; + CHECK((j_array.crbegin() + 3) == it); + CHECK((it - 3) == j_array.crbegin()); + CHECK((j_array.crbegin() - it) == 3); + CHECK(*it == json(3)); + it -= 2; + CHECK(*it == json(5)); + } + } + + SECTION("null") + { + { + auto it = j_null.rbegin(); + it += 3; + CHECK((j_null.rbegin() + 3) == it); + CHECK((it - 3) == j_null.rbegin()); + CHECK((j_null.rbegin() - it) == 3); + CHECK(it != j_null.rend()); + it -= 3; + CHECK(it == j_null.rend()); + } + { + auto it = j_null.crbegin(); + it += 3; + CHECK((j_null.crbegin() + 3) == it); + CHECK((it - 3) == j_null.crbegin()); + CHECK((j_null.crbegin() - it) == 3); + CHECK(it != j_null.crend()); + it -= 3; + CHECK(it == j_null.crend()); + } + } + + SECTION("value") + { + { + auto it = j_value.rbegin(); + it += 3; + CHECK((j_value.rbegin() + 3) == it); + CHECK((it - 3) == j_value.rbegin()); + CHECK((j_value.rbegin() - it) == 3); + CHECK(it != j_value.rend()); + it -= 3; + CHECK(*it == json(42)); + } + { + auto it = j_value.crbegin(); + it += 3; + CHECK((j_value.crbegin() + 3) == it); + CHECK((it - 3) == j_value.crbegin()); + CHECK((j_value.crbegin() - it) == 3); + CHECK(it != j_value.crend()); + it -= 3; + CHECK(*it == json(42)); + } + } + } + + SECTION("subscript operator") + { + SECTION("object") + { + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it[0], std::domain_error); + CHECK_THROWS_AS(it[1], std::domain_error); + CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); + CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it[0], std::domain_error); + CHECK_THROWS_AS(it[1], std::domain_error); + CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); + CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); + } + } + + SECTION("array") + { + { + auto it = j_array.rbegin(); + CHECK(it[0] == json(6)); + CHECK(it[1] == json(5)); + CHECK(it[2] == json(4)); + CHECK(it[3] == json(3)); + CHECK(it[4] == json(2)); + CHECK(it[5] == json(1)); + } + { + auto it = j_array.crbegin(); + CHECK(it[0] == json(6)); + CHECK(it[1] == json(5)); + CHECK(it[2] == json(4)); + CHECK(it[3] == json(3)); + CHECK(it[4] == json(2)); + CHECK(it[5] == json(1)); + } + } + + SECTION("null") + { + { + auto it = j_null.rbegin(); + CHECK_THROWS_AS(it[0], std::out_of_range); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[0], "cannot get value"); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + { + auto it = j_null.crbegin(); + CHECK_THROWS_AS(it[0], std::out_of_range); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[0], "cannot get value"); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + } + + SECTION("value") + { + { + auto it = j_value.rbegin(); + CHECK(it[0] == json(42)); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + { + auto it = j_value.crbegin(); + CHECK(it[0] == json(42)); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + } + } + } +} From 00046f6ff10ec0067cc2376a5b09434ceb04c026 Mon Sep 17 00:00:00 2001 From: Niels Date: Fri, 5 Aug 2016 08:18:19 +0200 Subject: [PATCH 12/46] fix for coveralls --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1ab50bfa..1d973fd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ matrix: - touch src/json.hpp - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - test/json_unit "*" - - coveralls --exclude test/src/catch.hpp --exclude test/src/unit-algorithms.cpp --exclude test/src/unit-allocator.cpp --exclude test/src/unit-capacity.cpp --exclude test/src/unit-class_const_iterator.cpp --exclude test/src/unit-class_iterator.cpp --exclude test/src/unit-class_lexer.cpp --exclude test/src/unit-class_parser.cpp --exclude test/src/unit-comparison.cpp --exclude test/src/unit-concepts.cpp --exclude test/src/unit-constructor1.cpp --exclude test/src/unit-constructor2.cpp --exclude test/src/unit-convenience.cpp --exclude test/src/unit-conversions.cpp --exclude test/src/unit-deserialization.cpp --exclude test/src/unit-element_access1.cpp --exclude test/src/unit-element_access2.cpp --exclude test/src/unit-inspection.cpp --exclude test/src/unit-iterator_wrapper.cpp --exclude test/src/unit-iterators1.cpp --exclude test/src/unit-iterators2.cpp --exclude test/src/unit-json_patch.cpp --exclude test/src/unit-json_pointer.cpp --exclude test/src/unit-modifiers.cpp --exclude test/src/unit-pointer_access.cpp --exclude test/src/unit-readme.cpp --exclude test/src/unit-reference_access.cpp --exclude test/src/unit-regression.cpp --exclude test/src/unit-serialization.cpp --exclude test/src/unit-testsuites.cpp --exclude test/src/unit-unicode.cpp --include src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' + - cd test ; coveralls --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' env: COMPILER=g++-4.9 - os: linux From d3c6ed08d6423da4393c0d5651ea6a8b31085782 Mon Sep 17 00:00:00 2001 From: Niels Date: Fri, 5 Aug 2016 08:40:42 +0200 Subject: [PATCH 13/46] set build-root --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1d973fd3..42428661 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,8 +25,7 @@ matrix: - touch src/json.hpp - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - test/json_unit "*" - - cd test ; coveralls --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' - env: COMPILER=g++-4.9 + - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' - os: linux compiler: gcc From fa4fd334b287d5e9d24589e9d12693ae479aa0f9 Mon Sep 17 00:00:00 2001 From: Niels Date: Fri, 5 Aug 2016 08:56:58 +0200 Subject: [PATCH 14/46] accidentially deleted env --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 42428661..929095be 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,7 @@ matrix: - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - test/json_unit "*" - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' + env: COMPILER=g++-4.9 - os: linux compiler: gcc From 0b34ddd47a5b06ec3a16246d03f60ffd39f8c5a8 Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 9 Aug 2016 18:19:54 +0200 Subject: [PATCH 15/46] another try for coveralls --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 929095be..63111591 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ matrix: after_success: - make clean - touch src/json.hpp - - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER + - make -C test CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - test/json_unit "*" - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' env: COMPILER=g++-4.9 From ff612e0e39b76a75e062f4355e6faa0c7a285a25 Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 9 Aug 2016 18:33:06 +0200 Subject: [PATCH 16/46] reverted last commit --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 63111591..929095be 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ matrix: after_success: - make clean - touch src/json.hpp - - make -C test CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER + - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - test/json_unit "*" - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' env: COMPILER=g++-4.9 From 117fd59abd1854bd4d90f5276cc4f82268a58ae2 Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 9 Aug 2016 22:18:13 +0200 Subject: [PATCH 17/46] first try --- .travis.yml | 371 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 212 insertions(+), 159 deletions(-) diff --git a/.travis.yml b/.travis.yml index 929095be..fc631eee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,175 +1,228 @@ +######################### +# project configuration # +######################### + +# C++ project language: cpp -dist: trusty -sudo: required +# container-based build +sudo: false -env: - global: - # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created - # via the "travis encrypt" command using the project repo's public key - - secure: "m89SSgE+ASLO38rSKx7MTXK3n5NkP9bIx95jwY71YEiuFzib30PDJ/DifKnXxBjvy/AkCGztErQRk/8ZCvq+4HXozU2knEGnL/RUitvlwbhzfh2D4lmS3BvWBGS3N3NewoPBrRmdcvnT0xjOGXxtZaJ3P74TkB9GBnlz/HmKORA=" +addons: + apt: + packages: + - g++-6 + sources: &sources + - ubuntu-toolchain-r-test + + +################ +# build matrix # +################ -# from http://stackoverflow.com/a/32127147/266378 matrix: include: - - os: linux - compiler: gcc - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-4.9', 'valgrind', 'python-pip', 'python-yaml'] - before_script: - - pip install --user git+git://github.com/eddyxu/cpp-coveralls.git - after_success: - - make clean - - touch src/json.hpp - - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - - test/json_unit "*" - - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' - env: COMPILER=g++-4.9 - - os: linux - compiler: gcc - before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-5', 'valgrind'] - coverity_scan: - project: - name: "nlohmann/json" - description: "Build submitted via Travis CI" - notification_email: niels.lohmann@gmail.com - build_command_prepend: "make clean ; sudo cp $(which g++-5) $(which g++)" - build_command: "make" - branch_pattern: coverity_scan - env: COMPILER=g++-5 + - os: osx + osx_image: xcode7.3 - - os: linux - compiler: gcc - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-6', 'valgrind'] - env: COMPILER=g++-6 + - os: osx + osx_image: xcode8 - # from https://github.com/travis-ci/travis-ci/issues/6120 - - os: linux - env: - - LLVM_VERSION=3.8.0 - - LLVM_ARCHIVE_PATH=$HOME/clang+llvm.tar.xz - - COMPILER=clang++ - - CPPFLAGS="-I $HOME/clang-$LLVM_VERSION/include/c++/v1" - - LDFLAGS=-lc++ - - PATH=$HOME/clang-$LLVM_VERSION/bin:$PATH - - LD_LIBRARY_PATH=$HOME/clang-$LLVM_VERSION/lib:$LD_LIBRARY_PATH - before_install: - - wget http://llvm.org/releases/$LLVM_VERSION/clang+llvm-$LLVM_VERSION-x86_64-linux-gnu-ubuntu-14.04.tar.xz -O $LLVM_ARCHIVE_PATH - - mkdir $HOME/clang-$LLVM_VERSION - - tar xf $LLVM_ARCHIVE_PATH -C $HOME/clang-$LLVM_VERSION --strip-components 1 + - os: linux + env: COMPILER=g++-6 + compiler: gcc - # Clang 3.5 is not able to compile the code, - # see https://travis-ci.org/nlohmann/json/jobs/126720186 -# - os: linux -# compiler: clang -# addons: -# apt: -# sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6'] -# packages: ['clang-3.6', 'valgrind'] -# env: COMPILER=clang++-3.6 -# -# - os: linux -# compiler: clang -# addons: -# apt: -# sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7'] -# packages: ['clang-3.7', 'valgrind'] -# env: COMPILER=clang++-3.7 -# -# - os: linux -# compiler: clang -# addons: -# apt: -# sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8'] -# packages: ['clang-3.8', 'valgrind'] -# env: COMPILER=clang++-3.8 +##################### +# installation step # +##################### - # - os: linux - # compiler: clang - # addons: - # apt: - # sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise'] - # packages: ['clang-3.9', 'valgrind'] - # env: COMPILER=clang++-3.9 +install: + - if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi - - os: osx - osx_image: beta-xcode6.1 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ - - os: osx - osx_image: beta-xcode6.2 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ - - - os: osx - osx_image: beta-xcode6.3 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ - - - os: osx - osx_image: xcode6.4 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ - - - os: osx - osx_image: xcode7.1 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ - - - os: osx - osx_image: xcode7.2 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ - - - os: osx - osx_image: xcode7.3 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ - - - os: osx - osx_image: xcode8 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ +################ +# build script # +################ script: - - uname -a - - $COMPILER --version - - make CXX=$COMPILER - - test/json_unit "*" - - if [ `which valgrind` ]; then - valgrind --error-exitcode=1 --leak-check=full test/json_unit ; - fi - - if [ `which brew` ]; then - brew update ; - brew tap nlohmann/json ; - brew install nlohmann_json --HEAD ; - brew test nlohmann_json ; - fi + - make + - test/json_unit + +#language: cpp +# +#dist: trusty +#sudo: required +# +#env: +# global: +# # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created +# # via the "travis encrypt" command using the project repo's public key +# - secure: "m89SSgE+ASLO38rSKx7MTXK3n5NkP9bIx95jwY71YEiuFzib30PDJ/DifKnXxBjvy/AkCGztErQRk/8ZCvq+4HXozU2knEGnL/RUitvlwbhzfh2D4lmS3BvWBGS3N3NewoPBrRmdcvnT0xjOGXxtZaJ3P74TkB9GBnlz/HmKORA=" +# +## from http://stackoverflow.com/a/32127147/266378 +#matrix: +# include: +# - os: linux +# compiler: gcc +# addons: +# apt: +# sources: ['ubuntu-toolchain-r-test'] +# packages: ['g++-4.9', 'valgrind', 'python-pip', 'python-yaml'] +# before_script: +# - pip install --user git+git://github.com/eddyxu/cpp-coveralls.git +# after_success: +# - make clean +# - touch src/json.hpp +# - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER +# - test/json_unit "*" +# - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' +# env: COMPILER=g++-4.9 +# +# - os: linux +# compiler: gcc +# before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt +# addons: +# apt: +# sources: ['ubuntu-toolchain-r-test'] +# packages: ['g++-5', 'valgrind'] +# coverity_scan: +# project: +# name: "nlohmann/json" +# description: "Build submitted via Travis CI" +# notification_email: niels.lohmann@gmail.com +# build_command_prepend: "make clean ; sudo cp $(which g++-5) $(which g++)" +# build_command: "make" +# branch_pattern: coverity_scan +# env: COMPILER=g++-5 +# +# - os: linux +# compiler: gcc +# addons: +# apt: +# sources: ['ubuntu-toolchain-r-test'] +# packages: ['g++-6', 'valgrind'] +# env: COMPILER=g++-6 +# +# # from https://github.com/travis-ci/travis-ci/issues/6120 +# - os: linux +# env: +# - LLVM_VERSION=3.8.0 +# - LLVM_ARCHIVE_PATH=$HOME/clang+llvm.tar.xz +# - COMPILER=clang++ +# - CPPFLAGS="-I $HOME/clang-$LLVM_VERSION/include/c++/v1" +# - LDFLAGS=-lc++ +# - PATH=$HOME/clang-$LLVM_VERSION/bin:$PATH +# - LD_LIBRARY_PATH=$HOME/clang-$LLVM_VERSION/lib:$LD_LIBRARY_PATH +# before_install: +# - wget http://llvm.org/releases/$LLVM_VERSION/clang+llvm-$LLVM_VERSION-x86_64-linux-gnu-ubuntu-14.04.tar.xz -O $LLVM_ARCHIVE_PATH +# - mkdir $HOME/clang-$LLVM_VERSION +# - tar xf $LLVM_ARCHIVE_PATH -C $HOME/clang-$LLVM_VERSION --strip-components 1 +# +# # Clang 3.5 is not able to compile the code, +# # see https://travis-ci.org/nlohmann/json/jobs/126720186 +# +## - os: linux +## compiler: clang +## addons: +## apt: +## sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6'] +## packages: ['clang-3.6', 'valgrind'] +## env: COMPILER=clang++-3.6 +## +## - os: linux +## compiler: clang +## addons: +## apt: +## sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7'] +## packages: ['clang-3.7', 'valgrind'] +## env: COMPILER=clang++-3.7 +## +## - os: linux +## compiler: clang +## addons: +## apt: +## sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8'] +## packages: ['clang-3.8', 'valgrind'] +## env: COMPILER=clang++-3.8 +# +# # - os: linux +# # compiler: clang +# # addons: +# # apt: +# # sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise'] +# # packages: ['clang-3.9', 'valgrind'] +# # env: COMPILER=clang++-3.9 +# +# - os: osx +# osx_image: beta-xcode6.1 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +# - os: osx +# osx_image: beta-xcode6.2 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +# - os: osx +# osx_image: beta-xcode6.3 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +# - os: osx +# osx_image: xcode6.4 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +# - os: osx +# osx_image: xcode7.1 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +# - os: osx +# osx_image: xcode7.2 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +# - os: osx +# osx_image: xcode7.3 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +# - os: osx +# osx_image: xcode8 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +#script: +# - uname -a +# - $COMPILER --version +# - make CXX=$COMPILER +# - test/json_unit "*" +# - if [ `which valgrind` ]; then +# valgrind --error-exitcode=1 --leak-check=full test/json_unit ; +# fi +# - if [ `which brew` ]; then +# brew update ; +# brew tap nlohmann/json ; +# brew install nlohmann_json --HEAD ; +# brew test nlohmann_json ; +# fi +# \ No newline at end of file From 4b37082e365103695fbfff01d412d78951f7bcad Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 9 Aug 2016 22:29:01 +0200 Subject: [PATCH 18/46] more GCC versions --- .travis.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index fc631eee..16716796 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,6 @@ sudo: false addons: apt: - packages: - - g++-6 sources: &sources - ubuntu-toolchain-r-test @@ -30,8 +28,18 @@ matrix: osx_image: xcode8 - os: linux - env: COMPILER=g++-6 compiler: gcc + env: COMPILER=g++-4.9 + addons: + apt: + packages: g++-4.9 + + - os: linux + compiler: gcc + env: COMPILER=g++-5 + addons: + apt: + packages: g++-5 ##################### @@ -47,6 +55,8 @@ install: ################ script: + - uname -a + - $CXX --version - make - test/json_unit From be05dbe6188013f82267ebf19d43fb68ca5ba77a Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 9 Aug 2016 22:33:31 +0200 Subject: [PATCH 19/46] moved sources --- .travis.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 16716796..2f541e42 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,11 +8,6 @@ language: cpp # container-based build sudo: false -addons: - apt: - sources: &sources - - ubuntu-toolchain-r-test - ################ # build matrix # @@ -32,6 +27,7 @@ matrix: env: COMPILER=g++-4.9 addons: apt: + sources: ['ubuntu-toolchain-r-test'] packages: g++-4.9 - os: linux @@ -39,6 +35,7 @@ matrix: env: COMPILER=g++-5 addons: apt: + sources: ['ubuntu-toolchain-r-test'] packages: g++-5 From 6150ffb9dce6fd2588d57a53c1de30ab9ee59d88 Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 9 Aug 2016 22:49:08 +0200 Subject: [PATCH 20/46] more compilers --- .travis.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2f541e42..e0b2db50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,12 +16,34 @@ sudo: false matrix: include: + # OSX / Clang + + - os: osx + osx_image: beta-xcode6.1 + + - os: osx + osx_image: beta-xcode6.2 + + - os: osx + osx_image: beta-xcode6.3 + + - os: osx + osx_image: xcode6.4 + + - os: osx + osx_image: xcode7.1 + + - os: osx + osx_image: xcode7.2 + - os: osx osx_image: xcode7.3 - os: osx osx_image: xcode8 + # Linux / GCC + - os: linux compiler: gcc env: COMPILER=g++-4.9 @@ -38,6 +60,14 @@ matrix: sources: ['ubuntu-toolchain-r-test'] packages: g++-5 + - os: linux + compiler: gcc + env: COMPILER=g++-6 + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: g++-6 + ##################### # installation step # @@ -56,6 +86,12 @@ script: - $CXX --version - make - test/json_unit + - if [ `which brew` ]; then + brew update ; + brew tap nlohmann/json ; + brew install nlohmann_json --HEAD ; + brew test nlohmann_json ; + fi #language: cpp # From b76861dde53ec85cd09d3467c3a8e94f33cae7f8 Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 9 Aug 2016 23:05:42 +0200 Subject: [PATCH 21/46] commit to re-fix issue #195 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e0b2db50..9b8feb6f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,8 @@ # C++ project language: cpp -# container-based build -sudo: false +dist: trusty +sudo: required ################ From b1c1fe9d39abbeb2c11fdac8238a265c916e3056 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 16:22:53 +0200 Subject: [PATCH 22/46] added Clang 3.8 --- .travis.yml | 35 +++++++++++++++++++++++++++++++++++ test/Makefile | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9b8feb6f..c102ab00 100644 --- a/.travis.yml +++ b/.travis.yml @@ -68,14 +68,49 @@ matrix: sources: ['ubuntu-toolchain-r-test'] packages: g++-6 + # Linux / Clang + + - os: linux + env: LLVM_VERSION=3.8.0 + compiler: clang ##################### # installation step # ##################### +cache: + directories: + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.8.0 + + install: + - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" + - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR} + - if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi + - | + if [[ "${LLVM_VERSION}" != "" ]]; then + LLVM_DIR=${DEPS_DIR}/llvm-${LLVM_VERSION} + if [[ -z "$(ls -A ${LLVM_DIR})" ]]; then + LLVM_URL="http://llvm.org/releases/${LLVM_VERSION}/llvm-${LLVM_VERSION}.src.tar.xz" + LIBCXX_URL="http://llvm.org/releases/${LLVM_VERSION}/libcxx-${LLVM_VERSION}.src.tar.xz" + LIBCXXABI_URL="http://llvm.org/releases/${LLVM_VERSION}/libcxxabi-${LLVM_VERSION}.src.tar.xz" + CLANG_URL="http://llvm.org/releases/${LLVM_VERSION}/clang+llvm-${LLVM_VERSION}-x86_64-linux-gnu-ubuntu-14.04.tar.xz" + mkdir -p ${LLVM_DIR} ${LLVM_DIR}/build ${LLVM_DIR}/projects/libcxx ${LLVM_DIR}/projects/libcxxabi ${LLVM_DIR}/clang + travis_retry wget --quiet -O - ${LLVM_URL} | tar --strip-components=1 -xJ -C ${LLVM_DIR} + travis_retry wget --quiet -O - ${LIBCXX_URL} | tar --strip-components=1 -xJ -C ${LLVM_DIR}/projects/libcxx + travis_retry wget --quiet -O - ${LIBCXXABI_URL} | tar --strip-components=1 -xJ -C ${LLVM_DIR}/projects/libcxxabi + travis_retry wget --quiet -O - ${CLANG_URL} | tar --strip-components=1 -xJ -C ${LLVM_DIR}/clang + (cd ${LLVM_DIR}/build && cmake .. -DCMAKE_INSTALL_PREFIX=${LLVM_DIR}/install -DCMAKE_CXX_COMPILER=clang++) + (cd ${LLVM_DIR}/build/projects/libcxx && make install -j2) + (cd ${LLVM_DIR}/build/projects/libcxxabi && make install -j2) + fi + export CXXFLAGS="-nostdinc++ -isystem ${LLVM_DIR}/install/include/c++/v1" + export LDFLAGS="-L ${LLVM_DIR}/install/lib -l c++ -l c++abi" + export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${LLVM_DIR}/install/lib" + export PATH="${LLVM_DIR}/clang/bin:${PATH}" + fi ################ # build script # diff --git a/test/Makefile b/test/Makefile index 7bc8274f..8e1ad6a0 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,7 +3,7 @@ ########################################################################## # additional flags -CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wno-ctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wno-keyword-macro -Wno-float-equal +CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wno-ctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wno-float-equal CPPFLAGS += -I ../src -I . SOURCES = src/unit.cpp \ From 407e8dbb8e318033e905a7545b73c267cb9bae8c Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 16:25:35 +0200 Subject: [PATCH 23/46] fixed YAML error --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index c102ab00..3cdf45f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,9 +70,9 @@ matrix: # Linux / Clang - - os: linux - env: LLVM_VERSION=3.8.0 - compiler: clang + - os: linux + env: LLVM_VERSION=3.8.0 + compiler: clang ##################### # installation step # From 31963723d3692c22d7f4cb866e06a259ca23c6d9 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 16:35:57 +0200 Subject: [PATCH 24/46] no directory change --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3cdf45f2..de6e25b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -85,7 +85,7 @@ cache: install: - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" - - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR} + - mkdir -p ${DEPS_DIR} - if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi From 997bc5d1ab216dbdf2877a7237aecae7c477ee52 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 16:56:05 +0200 Subject: [PATCH 25/46] more clang versions --- .travis.yml | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/.travis.yml b/.travis.yml index de6e25b1..b204d375 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,17 +70,62 @@ matrix: # Linux / Clang + - os: linux + env: LLVM_VERSION=3.5.0 + compiler: clang + + - os: linux + env: LLVM_VERSION=3.5.1 + compiler: clang + + - os: linux + env: LLVM_VERSION=3.5.2 + compiler: clang + + - os: linux + env: LLVM_VERSION=3.6.0 + compiler: clang + + - os: linux + env: LLVM_VERSION=3.6.1 + compiler: clang + + - os: linux + env: LLVM_VERSION=3.6.2 + compiler: clang + + - os: linux + env: LLVM_VERSION=3.7.0 + compiler: clang + + - os: linux + env: LLVM_VERSION=3.7.1 + compiler: clang + - os: linux env: LLVM_VERSION=3.8.0 compiler: clang + - os: linux + env: LLVM_VERSION=3.8.1 + compiler: clang + ##################### # installation step # ##################### cache: directories: + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.5.2 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.5.1 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.5.0 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.2 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.1 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.0 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.7.0 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.7.1 - ${TRAVIS_BUILD_DIR}/deps/llvm-3.8.0 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.8.1 install: From 4d90331718f015b7ee90d914690b9412d740fc0a Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 17:42:33 +0200 Subject: [PATCH 26/46] a test --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b204d375..344aa219 100644 --- a/.travis.yml +++ b/.travis.yml @@ -151,7 +151,7 @@ install: (cd ${LLVM_DIR}/build/projects/libcxx && make install -j2) (cd ${LLVM_DIR}/build/projects/libcxxabi && make install -j2) fi - export CXXFLAGS="-nostdinc++ -isystem ${LLVM_DIR}/install/include/c++/v1" + export CXXFLAGS="-stdlib=libc++ -nostdinc++ -isystem ${LLVM_DIR}/install/include/c++/v1" export LDFLAGS="-L ${LLVM_DIR}/install/lib -l c++ -l c++abi" export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${LLVM_DIR}/install/lib" export PATH="${LLVM_DIR}/clang/bin:${PATH}" From a2e923de32d04012f201c2cc8fe4b79e425b5608 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 18:38:31 +0200 Subject: [PATCH 27/46] removed clang 3.5.x --- .travis.yml | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 344aa219..f1db0a25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,18 +70,6 @@ matrix: # Linux / Clang - - os: linux - env: LLVM_VERSION=3.5.0 - compiler: clang - - - os: linux - env: LLVM_VERSION=3.5.1 - compiler: clang - - - os: linux - env: LLVM_VERSION=3.5.2 - compiler: clang - - os: linux env: LLVM_VERSION=3.6.0 compiler: clang @@ -116,9 +104,6 @@ matrix: cache: directories: - - ${TRAVIS_BUILD_DIR}/deps/llvm-3.5.2 - - ${TRAVIS_BUILD_DIR}/deps/llvm-3.5.1 - - ${TRAVIS_BUILD_DIR}/deps/llvm-3.5.0 - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.2 - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.1 - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.0 @@ -151,7 +136,7 @@ install: (cd ${LLVM_DIR}/build/projects/libcxx && make install -j2) (cd ${LLVM_DIR}/build/projects/libcxxabi && make install -j2) fi - export CXXFLAGS="-stdlib=libc++ -nostdinc++ -isystem ${LLVM_DIR}/install/include/c++/v1" + export CXXFLAGS="-nostdinc++ -isystem ${LLVM_DIR}/install/include/c++/v1" export LDFLAGS="-L ${LLVM_DIR}/install/lib -l c++ -l c++abi" export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${LLVM_DIR}/install/lib" export PATH="${LLVM_DIR}/clang/bin:${PATH}" @@ -166,6 +151,9 @@ script: - $CXX --version - make - test/json_unit + - if [ `which valgrind` ]; then + valgrind --error-exitcode=1 --leak-check=full test/json_unit ; + fi - if [ `which brew` ]; then brew update ; brew tap nlohmann/json ; From efe1d52629b6d661b39cf1774285096c940f7ce5 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 18:54:19 +0200 Subject: [PATCH 28/46] added coverity, coveralls, and valgrind --- .travis.yml | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index f1db0a25..2e6d4a1c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,17 @@ dist: trusty sudo: required +################### +# global settings # +################### + +env: + global: + # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created + # via the "travis encrypt" command using the project repo's public key + - secure: "m89SSgE+ASLO38rSKx7MTXK3n5NkP9bIx95jwY71YEiuFzib30PDJ/DifKnXxBjvy/AkCGztErQRk/8ZCvq+4HXozU2knEGnL/RUitvlwbhzfh2D4lmS3BvWBGS3N3NewoPBrRmdcvnT0xjOGXxtZaJ3P74TkB9GBnlz/HmKORA=" + + ################ # build matrix # ################ @@ -98,10 +109,66 @@ matrix: env: LLVM_VERSION=3.8.1 compiler: clang + # Valgrind + + - os: linux + compiler: gcc + env: + - COMPILER=g++-4.9 + - SPECIAL=valgrind + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: [g++-4.9, valgrind] + after_success: + - valgrind --error-exitcode=1 --leak-check=full test/json_unit * ; + + # Coveralls + + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.9', 'valgrind', 'python-pip', 'python-yaml'] + before_script: + - pip install --user git+git://github.com/eddyxu/cpp-coveralls.git + after_success: + - make clean + - touch src/json.hpp + - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER + - test/json_unit "*" + - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' + env: + - COMPILER=g++-4.9 + - SPECIAL=coveralls + + # Coverity (only for branch coverity_scan) + + - os: linux + compiler: gcc + before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-5', 'valgrind'] + coverity_scan: + project: + name: "nlohmann/json" + description: "Build submitted via Travis CI" + notification_email: niels.lohmann@gmail.com + build_command_prepend: "make clean ; sudo cp $(which g++-5) $(which g++)" + build_command: "make" + branch_pattern: coverity_scan + env: + - COMPILER=g++-5 + - SPECIAL=coverity + ##################### # installation step # ##################### +# set directories to cache cache: directories: - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.2 @@ -114,11 +181,14 @@ cache: install: + # create deps dir if not existing - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" - mkdir -p ${DEPS_DIR} + # make sure CXX is correctly set - if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi + # install LLVM/clang when LLVM_VERSION is set - | if [[ "${LLVM_VERSION}" != "" ]]; then LLVM_DIR=${DEPS_DIR}/llvm-${LLVM_VERSION} @@ -147,13 +217,17 @@ install: ################ script: + # show OS/compiler version - uname -a - $CXX --version + + # compile - make - - test/json_unit - - if [ `which valgrind` ]; then - valgrind --error-exitcode=1 --leak-check=full test/json_unit ; - fi + + # execute unit tests + - test/json_unit "*" + + # check if homebrew works (only checks develop branch) - if [ `which brew` ]; then brew update ; brew tap nlohmann/json ; From ce30526ee8bdf057ac81d5fa0c0d00e7eb683c61 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 19:35:27 +0200 Subject: [PATCH 29/46] fixed Valgrind call --- .travis.yml | 131 +--------------------------------------------------- 1 file changed, 1 insertion(+), 130 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2e6d4a1c..903a4c58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -121,7 +121,7 @@ matrix: sources: ['ubuntu-toolchain-r-test'] packages: [g++-4.9, valgrind] after_success: - - valgrind --error-exitcode=1 --leak-check=full test/json_unit * ; + - valgrind --error-exitcode=1 --leak-check=full test/json_unit "*" ; # Coveralls @@ -282,132 +282,3 @@ script: # branch_pattern: coverity_scan # env: COMPILER=g++-5 # -# - os: linux -# compiler: gcc -# addons: -# apt: -# sources: ['ubuntu-toolchain-r-test'] -# packages: ['g++-6', 'valgrind'] -# env: COMPILER=g++-6 -# -# # from https://github.com/travis-ci/travis-ci/issues/6120 -# - os: linux -# env: -# - LLVM_VERSION=3.8.0 -# - LLVM_ARCHIVE_PATH=$HOME/clang+llvm.tar.xz -# - COMPILER=clang++ -# - CPPFLAGS="-I $HOME/clang-$LLVM_VERSION/include/c++/v1" -# - LDFLAGS=-lc++ -# - PATH=$HOME/clang-$LLVM_VERSION/bin:$PATH -# - LD_LIBRARY_PATH=$HOME/clang-$LLVM_VERSION/lib:$LD_LIBRARY_PATH -# before_install: -# - wget http://llvm.org/releases/$LLVM_VERSION/clang+llvm-$LLVM_VERSION-x86_64-linux-gnu-ubuntu-14.04.tar.xz -O $LLVM_ARCHIVE_PATH -# - mkdir $HOME/clang-$LLVM_VERSION -# - tar xf $LLVM_ARCHIVE_PATH -C $HOME/clang-$LLVM_VERSION --strip-components 1 -# -# # Clang 3.5 is not able to compile the code, -# # see https://travis-ci.org/nlohmann/json/jobs/126720186 -# -## - os: linux -## compiler: clang -## addons: -## apt: -## sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6'] -## packages: ['clang-3.6', 'valgrind'] -## env: COMPILER=clang++-3.6 -## -## - os: linux -## compiler: clang -## addons: -## apt: -## sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7'] -## packages: ['clang-3.7', 'valgrind'] -## env: COMPILER=clang++-3.7 -## -## - os: linux -## compiler: clang -## addons: -## apt: -## sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8'] -## packages: ['clang-3.8', 'valgrind'] -## env: COMPILER=clang++-3.8 -# -# # - os: linux -# # compiler: clang -# # addons: -# # apt: -# # sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise'] -# # packages: ['clang-3.9', 'valgrind'] -# # env: COMPILER=clang++-3.9 -# -# - os: osx -# osx_image: beta-xcode6.1 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -# - os: osx -# osx_image: beta-xcode6.2 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -# - os: osx -# osx_image: beta-xcode6.3 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -# - os: osx -# osx_image: xcode6.4 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -# - os: osx -# osx_image: xcode7.1 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -# - os: osx -# osx_image: xcode7.2 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -# - os: osx -# osx_image: xcode7.3 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -# - os: osx -# osx_image: xcode8 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -#script: -# - uname -a -# - $COMPILER --version -# - make CXX=$COMPILER -# - test/json_unit "*" -# - if [ `which valgrind` ]; then -# valgrind --error-exitcode=1 --leak-check=full test/json_unit ; -# fi -# - if [ `which brew` ]; then -# brew update ; -# brew tap nlohmann/json ; -# brew install nlohmann_json --HEAD ; -# brew test nlohmann_json ; -# fi -# \ No newline at end of file From d02e67d4a957d050ab4233209c452fdf433b238a Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 20:56:26 +0200 Subject: [PATCH 30/46] coveralls with lcov --- .gitignore | 12 ++--- .travis.yml | 134 +++++++++++++++++++++++++++++--------------------- test/Makefile | 2 +- 3 files changed, 85 insertions(+), 63 deletions(-) diff --git a/.gitignore b/.gitignore index b82214e9..c7e847c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,16 @@ json_unit json_benchmarks - fuzz-testing *.dSYM +*.o +*.gcno +*.gcda working -html +doc/xml +doc/html me.nlohmann.json.docset -android -doc/xml - benchmarks/files/numbers/*.json - -*.o diff --git a/.travis.yml b/.travis.yml index 903a4c58..03da02d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,85 @@ env: matrix: include: + # Valgrind + + - os: linux + compiler: gcc + env: + - COMPILER=g++-4.9 + - SPECIAL=valgrind + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: [g++-4.9, valgrind] + after_success: + - valgrind --error-exitcode=1 --leak-check=full test/json_unit -s "*" ; + + # Coveralls + + #- os: linux + # compiler: gcc + # addons: + # apt: + # sources: ['ubuntu-toolchain-r-test'] + # packages: ['g++-4.9', 'valgrind', 'python-pip', 'python-yaml'] + # before_script: + # - pip install --user git+git://github.com/eddyxu/cpp-coveralls.git + # after_success: + # - make clean + # - touch src/json.hpp + # - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER + # - test/json_unit "*" + # - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' + # env: + # - COMPILER=g++-4.9 + # - SPECIAL=coveralls + + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.9', 'rubygems'] + before_script: + - wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.11.orig.tar.gz + - tar xf lcov_1.11.orig.tar.gz + - sudo make -C lcov-1.11/ install + - gem install coveralls-lcov + after_success: + - make clean + - CXXFLAGS="--coverage -g -O0" CPPFLAGS="-DNDEBUG" make + - test/json_unit "*" + - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' + - lcov --directory src --directory test/src --capture --output-file coverage.info --rc lcov_branch_coverage=1 --no-external + - lcov --remove coverage.info 'test/src/*' --output-file coverage.info --rc lcov_branch_coverage=1 + - lcov --list coverage.info --rc lcov_branch_coverage=1 + - coveralls-lcov --repo-token F9bs4Nop10JRgqPQXRcifyQKYhb3FczkS coverage.info + env: + - COMPILER=g++-4.9 + - SPECIAL=coveralls + + # Coverity (only for branch coverity_scan) + + - os: linux + compiler: gcc + before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-5', 'valgrind'] + coverity_scan: + project: + name: "nlohmann/json" + description: "Build submitted via Travis CI" + notification_email: niels.lohmann@gmail.com + build_command_prepend: "make clean ; sudo cp $(which g++-5) $(which g++)" + build_command: "make" + branch_pattern: coverity_scan + env: + - COMPILER=g++-5 + - SPECIAL=coverity + # OSX / Clang - os: osx @@ -109,61 +188,6 @@ matrix: env: LLVM_VERSION=3.8.1 compiler: clang - # Valgrind - - - os: linux - compiler: gcc - env: - - COMPILER=g++-4.9 - - SPECIAL=valgrind - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: [g++-4.9, valgrind] - after_success: - - valgrind --error-exitcode=1 --leak-check=full test/json_unit "*" ; - - # Coveralls - - - os: linux - compiler: gcc - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-4.9', 'valgrind', 'python-pip', 'python-yaml'] - before_script: - - pip install --user git+git://github.com/eddyxu/cpp-coveralls.git - after_success: - - make clean - - touch src/json.hpp - - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - - test/json_unit "*" - - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' - env: - - COMPILER=g++-4.9 - - SPECIAL=coveralls - - # Coverity (only for branch coverity_scan) - - - os: linux - compiler: gcc - before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-5', 'valgrind'] - coverity_scan: - project: - name: "nlohmann/json" - description: "Build submitted via Travis CI" - notification_email: niels.lohmann@gmail.com - build_command_prepend: "make clean ; sudo cp $(which g++-5) $(which g++)" - build_command: "make" - branch_pattern: coverity_scan - env: - - COMPILER=g++-5 - - SPECIAL=coverity - ##################### # installation step # ##################### diff --git a/test/Makefile b/test/Makefile index 8e1ad6a0..ead1f073 100644 --- a/test/Makefile +++ b/test/Makefile @@ -51,4 +51,4 @@ json_unit: $(OBJECTS) ../src/json.hpp src/catch.hpp @$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ clean: - rm -fr json_unit $(OBJECTS) + rm -fr json_unit $(OBJECTS) $(SOURCES:.cpp=.gcno) $(SOURCES:.cpp=.gcda) From 1c4ca6d7b1ca3caec06ca81f57abb1b2e15c41e7 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 21:00:36 +0200 Subject: [PATCH 31/46] rubygems -> ruby --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 03da02d5..ce3fc057 100644 --- a/.travis.yml +++ b/.travis.yml @@ -66,7 +66,7 @@ matrix: addons: apt: sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-4.9', 'rubygems'] + packages: ['g++-4.9', 'ruby'] before_script: - wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.11.orig.tar.gz - tar xf lcov_1.11.orig.tar.gz From 5db41313ba4b8652c90af2294871a662387210e9 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 21:33:03 +0200 Subject: [PATCH 32/46] valgrind + full unit tests takes too long --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ce3fc057..59f07481 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,7 @@ matrix: sources: ['ubuntu-toolchain-r-test'] packages: [g++-4.9, valgrind] after_success: - - valgrind --error-exitcode=1 --leak-check=full test/json_unit -s "*" ; + - valgrind --error-exitcode=1 --leak-check=full test/json_unit # Coveralls From 46174879efbd6bad0c8d5105429562ab3cb1e9df Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 22:41:09 +0200 Subject: [PATCH 33/46] clean up --- .travis.yml | 20 +--- README.md | 8 +- test/src/unit-pointer_access.cpp | 190 ++++++++++++++++++++++++++++++- 3 files changed, 193 insertions(+), 25 deletions(-) diff --git a/.travis.yml b/.travis.yml index 59f07481..ad1d12d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,25 +41,7 @@ matrix: after_success: - valgrind --error-exitcode=1 --leak-check=full test/json_unit - # Coveralls - - #- os: linux - # compiler: gcc - # addons: - # apt: - # sources: ['ubuntu-toolchain-r-test'] - # packages: ['g++-4.9', 'valgrind', 'python-pip', 'python-yaml'] - # before_script: - # - pip install --user git+git://github.com/eddyxu/cpp-coveralls.git - # after_success: - # - make clean - # - touch src/json.hpp - # - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - # - test/json_unit "*" - # - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' - # env: - # - COMPILER=g++-4.9 - # - SPECIAL=coveralls + # Coveralls (http://gronlier.fr/blog/2015/01/adding-code-coverage-to-your-c-project/) - os: linux compiler: gcc diff --git a/README.md b/README.md index 722bf880..c6fa73b4 100644 --- a/README.md +++ b/README.md @@ -416,7 +416,13 @@ The following compilers are currently used in continuous integration at [Travis] | GCC 4.9.3 | Ubuntu 14.04.4 LTS | g++-4.9 (Ubuntu 4.9.3-8ubuntu2~14.04) 4.9.3 | | GCC 5.3.0 | Ubuntu 14.04.4 LTS | g++-5 (Ubuntu 5.3.0-3ubuntu1~14.04) 5.3.0 20151204 | | GCC 6.1.1 | Ubuntu 14.04.4 LTS | g++-6 (Ubuntu 6.1.1-3ubuntu11~14.04.1) 6.1.1 20160511 | +| Clang 3.6.0 | Ubuntu 14.04.4 LTS | clang version 3.6.0 (tags/RELEASE_360/final) | +| Clang 3.6.1 | Ubuntu 14.04.4 LTS | clang version 3.6.1 (tags/RELEASE_361/final) | +| Clang 3.6.2 | Ubuntu 14.04.4 LTS | clang version 3.6.2 (tags/RELEASE_362/final) | +| Clang 3.7.0 | Ubuntu 14.04.4 LTS | clang version 3.7.0 (tags/RELEASE_370/final) | +| Clang 3.7.1 | Ubuntu 14.04.4 LTS | clang version 3.7.1 (tags/RELEASE_371/final) | | Clang 3.8.0 | Ubuntu 14.04.4 LTS | clang version 3.8.0 (tags/RELEASE_380/final) | +| Clang 3.8.1 | Ubuntu 14.04.4 LTS | clang version 3.8.1 (tags/RELEASE_381/final) | | Clang Xcode 6.1 | Darwin Kernel Version 13.4.0 (OSX 10.9.5) | Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn) | | Clang Xcode 6.2 | Darwin Kernel Version 13.4.0 (OSX 10.9.5) | Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn) | | Clang Xcode 6.3 | Darwin Kernel Version 14.3.0 (OSX 10.10.3) | Apple LLVM version 6.1.0 (clang-602.0.49) (based on LLVM 3.6.0svn) | @@ -504,7 +510,7 @@ To compile and run the tests, you need to execute $ make check =============================================================================== -All tests passed (8905012 assertions in 32 test cases) +All tests passed (8905099 assertions in 32 test cases) ``` For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml). diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp index bae9ee4c..9353b5b0 100644 --- a/test/src/unit-pointer_access.cpp +++ b/test/src/unit-pointer_access.cpp @@ -79,18 +79,30 @@ TEST_CASE("pointer access") SECTION("pointer access to const object_t") { - using test_type = json::object_t; + using test_type = const json::object_t; const json value = {{"one", 1}, {"two", 2}}; - // this should not compile - // test_type* p1 = value.get_ptr(); - // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); - CHECK(p2 == p3); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to array_t") @@ -121,6 +133,34 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); } + SECTION("pointer access to const array_t") + { + using test_type = const json::array_t; + const json value = {1, 2, 3, 4}; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + SECTION("pointer access to string_t") { using test_type = json::string_t; @@ -149,6 +189,34 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); } + SECTION("pointer access to const string_t") + { + using test_type = const json::string_t; + const json value = "hello"; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + SECTION("pointer access to boolean_t") { using test_type = json::boolean_t; @@ -177,6 +245,34 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); } + SECTION("pointer access to const boolean_t") + { + using test_type = const json::boolean_t; + const json value = false; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + //CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + //CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + //CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + SECTION("pointer access to number_integer_t") { using test_type = json::number_integer_t; @@ -205,6 +301,34 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); } + SECTION("pointer access to const number_integer_t") + { + using test_type = const json::number_integer_t; + const json value = 23; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + SECTION("pointer access to number_unsigned_t") { using test_type = json::number_unsigned_t; @@ -233,6 +357,34 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); } + SECTION("pointer access to const number_unsigned_t") + { + using test_type = const json::number_unsigned_t; + const json value = 23u; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + } + SECTION("pointer access to number_float_t") { using test_type = json::number_float_t; @@ -260,4 +412,32 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); } + + SECTION("pointer access to const number_float_t") + { + using test_type = const json::number_float_t; + const json value = 42.23; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == Approx(value.get())); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == Approx(value.get())); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == Approx(value.get())); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + } } From 03de590372d73efae2a5bab1cd72bf3a276e1d5b Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 11 Aug 2016 20:52:48 +0200 Subject: [PATCH 34/46] improved documentation for #289 --- README.md | 2 +- src/json.hpp | 19 ++++++++++++++----- src/json.hpp.re2c | 19 ++++++++++++++----- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index c6fa73b4..de70912b 100644 --- a/README.md +++ b/README.md @@ -498,7 +498,7 @@ Thanks a lot for helping out! ## Notes -- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](http://en.cppreference.com/w/cpp/error/assert). +- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](http://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a2e26bd0b0168abb61f67ad5bcd5b9fa1.html#a2e26bd0b0168abb61f67ad5bcd5b9fa1) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a674de1ee73e6bf4843fc5dc1351fb726.html#a674de1ee73e6bf4843fc5dc1351fb726). - As the exact type of a number is not defined in the [JSON specification](http://rfc7159.net/rfc7159), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions. diff --git a/src/json.hpp b/src/json.hpp index 878fb899..5ab8ec26 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -1843,7 +1843,8 @@ class basic_json @param[in] first begin of the range to copy from (included) @param[in] last end of the range to copy from (excluded) - @pre Iterators @a first and @a last must be initialized. + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion.** @throw std::domain_error if iterators are not compatible; that is, do not belong to the same JSON value; example: `"iterators are not compatible"` @@ -3509,6 +3510,9 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @@ -3667,6 +3671,9 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @@ -3867,7 +3874,8 @@ class basic_json @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). + or an empty array or object (undefined behavior, **guarded by + assertions**). @post The JSON value remains unchanged. @throw std::out_of_range when called on `null` value @@ -3909,7 +3917,8 @@ class basic_json @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). + or an empty array or object (undefined behavior, **guarded by + assertions**). @post The JSON value remains unchanged. @throw std::out_of_range when called on `null` value. @@ -6592,8 +6601,8 @@ class basic_json @note An iterator is called *initialized* when a pointer to a JSON value has been set (e.g., by a constructor or a copy assignment). If the iterator is default-constructed, it is *uninitialized* and most - methods are undefined. The library uses assertions to detect calls - on uninitialized iterators. + methods are undefined. **The library uses assertions to detect calls + on uninitialized iterators.** @requirement The class satisfies the following concept requirements: - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 32482ea8..0d8ffa15 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -1843,7 +1843,8 @@ class basic_json @param[in] first begin of the range to copy from (included) @param[in] last end of the range to copy from (excluded) - @pre Iterators @a first and @a last must be initialized. + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion.** @throw std::domain_error if iterators are not compatible; that is, do not belong to the same JSON value; example: `"iterators are not compatible"` @@ -3509,6 +3510,9 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @@ -3667,6 +3671,9 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @@ -3867,7 +3874,8 @@ class basic_json @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). + or an empty array or object (undefined behavior, **guarded by + assertions**). @post The JSON value remains unchanged. @throw std::out_of_range when called on `null` value @@ -3909,7 +3917,8 @@ class basic_json @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). + or an empty array or object (undefined behavior, **guarded by + assertions**). @post The JSON value remains unchanged. @throw std::out_of_range when called on `null` value. @@ -6592,8 +6601,8 @@ class basic_json @note An iterator is called *initialized* when a pointer to a JSON value has been set (e.g., by a constructor or a copy assignment). If the iterator is default-constructed, it is *uninitialized* and most - methods are undefined. The library uses assertions to detect calls - on uninitialized iterators. + methods are undefined. **The library uses assertions to detect calls + on uninitialized iterators.** @requirement The class satisfies the following concept requirements: - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): From ca80a71c2852207e08c99ea016b7a0ef271d69bf Mon Sep 17 00:00:00 2001 From: Niels Date: Mon, 15 Aug 2016 21:45:49 +0200 Subject: [PATCH 35/46] added notes from the CII Best Practices badge --- .github/CONTRIBUTING.md | 5 +++++ README.md | 1 + src/json.hpp | 4 ++-- src/json.hpp.re2c | 4 ++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 66420e9a..2b416081 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -6,6 +6,10 @@ This project started as a little excuse to exercise some of the cool new C++11 f To make it as easy as possible for you to contribute and for me to keep an overview, here are a few guidelines which should help us avoid all kinds of unnecessary work or disappointment. And of course, this document is subject to discussion, so please [create an issue](https://github.com/nlohmann/json/issues/new) or a pull request if you find a way to improve it! +## Private reports + +Usually, all issues are tracked publicly on [Github](https://github.com/nlohmann/json/issues). If you want to make a private report (e.g., for a vulnerability or to attach an example that is not meant to be publisheed), please send an email to . + ## Prerequisites Please [create an issue](https://github.com/nlohmann/json/issues/new), assuming one does not already exist, and describe your concern. Note you need a [GitHub account](https://github.com/signup/free) for this. @@ -57,6 +61,7 @@ Please understand that I cannot accept pull requests changing only file `src/jso ## Note - If you open a pull request, the code will be automatically tested with [Valgrind](http://valgrind.org)'s Memcheck tool to detect memory leaks. Please be aware that the execution with Valgrind _may_ in rare cases yield different behavior than running the code directly. This can result in failing unit tests which run successfully without Valgrind. +- There is a Makefile target `make pretty` which runs [Artistic Style](http://astyle.sourceforge.net) to fix indentation. If possible, run it before opening the pull request. Otherwise, we shall run it afterward. ## Please don't diff --git a/README.md b/README.md index de70912b..73a03363 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) [![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases) [![Github Issues](https://img.shields.io/github/issues/nlohmann/json.svg)](http://github.com/nlohmann/json/issues) +[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/289/badge)](https://bestpractices.coreinfrastructure.org/projects/289) ## Design goals diff --git a/src/json.hpp b/src/json.hpp index 5ab8ec26..ddf37eb2 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -9484,7 +9484,7 @@ basic_json_parser_63: } /// split the string input to reference tokens - static std::vector split(std::string reference_string) + static std::vector split(const std::string& reference_string) { std::vector result; @@ -10212,7 +10212,7 @@ basic_json_parser_63: */ static basic_json diff(const basic_json& source, const basic_json& target, - std::string path = "") + const std::string& path = "") { // the patch basic_json result(value_t::array); diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 0d8ffa15..740c768c 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8781,7 +8781,7 @@ class basic_json } /// split the string input to reference tokens - static std::vector split(std::string reference_string) + static std::vector split(const std::string& reference_string) { std::vector result; @@ -9509,7 +9509,7 @@ class basic_json */ static basic_json diff(const basic_json& source, const basic_json& target, - std::string path = "") + const std::string& path = "") { // the patch basic_json result(value_t::array); From 35f22e85962fb1b325ba595b7d696449fdd0c435 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 17 Aug 2016 21:38:19 +0200 Subject: [PATCH 36/46] checking MSVC compiler flags --- test/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 782d5b53..9fe0327c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -38,8 +38,8 @@ add_executable(${JSON_UNITTEST_TARGET_NAME} set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON - COMPILE_DEFINITIONS "$<$:_SCL_SECURE_NO_WARNINGS>" - COMPILE_OPTIONS "$<$:/EHsc;$<$:/Od>>" + #COMPILE_DEFINITIONS "$<$:_SCL_SECURE_NO_WARNINGS>" + COMPILE_OPTIONS "$<$:/EHsc /Wall;$<$:/Od>>" ) target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src") From c0922c7aac3ffacbfcde50eddb45ce23006e5b1e Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 17 Aug 2016 21:43:28 +0200 Subject: [PATCH 37/46] /Wall --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9fe0327c..540d9db1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -39,7 +39,7 @@ set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON #COMPILE_DEFINITIONS "$<$:_SCL_SECURE_NO_WARNINGS>" - COMPILE_OPTIONS "$<$:/EHsc /Wall;$<$:/Od>>" + COMPILE_OPTIONS "$<$:/EHsc;/Wall$<$:/Od>>" ) target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src") From f40f81c87eb9cfb7b3983e8d71c9ec5ba82db87d Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 17 Aug 2016 21:44:53 +0200 Subject: [PATCH 38/46] forgot a semicolon --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 540d9db1..febb0695 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -39,7 +39,7 @@ set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON #COMPILE_DEFINITIONS "$<$:_SCL_SECURE_NO_WARNINGS>" - COMPILE_OPTIONS "$<$:/EHsc;/Wall$<$:/Od>>" + COMPILE_OPTIONS "$<$:/EHsc;/Wall;$<$:/Od>>" ) target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src") From 628a5eae500b8320aec3f0fbb4a5eba48d320e03 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 17 Aug 2016 22:33:26 +0200 Subject: [PATCH 39/46] reset build file --- test/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index febb0695..782d5b53 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -38,8 +38,8 @@ add_executable(${JSON_UNITTEST_TARGET_NAME} set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON - #COMPILE_DEFINITIONS "$<$:_SCL_SECURE_NO_WARNINGS>" - COMPILE_OPTIONS "$<$:/EHsc;/Wall;$<$:/Od>>" + COMPILE_DEFINITIONS "$<$:_SCL_SECURE_NO_WARNINGS>" + COMPILE_OPTIONS "$<$:/EHsc;$<$:/Od>>" ) target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src") From 039cedaf8e2884d136b70aae1f3a2b80640b2583 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 17 Aug 2016 23:14:28 +0200 Subject: [PATCH 40/46] changes to address #295 --- src/json.hpp | 6 ++++-- src/json.hpp.re2c | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index ddf37eb2..a8289a49 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8892,7 +8892,8 @@ basic_json_parser_63: { case lexer::token_type::begin_object: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::object_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) { // explicitly set result to object to cope with {} result.m_type = value_t::object; @@ -8970,7 +8971,8 @@ basic_json_parser_63: case lexer::token_type::begin_array: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::array_start, result)) != 0))) { // explicitly set result to object to cope with [] result.m_type = value_t::array; diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 740c768c..ffa20f39 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8189,7 +8189,8 @@ class basic_json { case lexer::token_type::begin_object: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::object_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) { // explicitly set result to object to cope with {} result.m_type = value_t::object; @@ -8267,7 +8268,8 @@ class basic_json case lexer::token_type::begin_array: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::array_start, result)) != 0))) { // explicitly set result to object to cope with [] result.m_type = value_t::array; From d2564c6100062476182006fc9e3ba34d2078facf Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 18 Aug 2016 18:29:19 +0200 Subject: [PATCH 41/46] added cppcheck target for travis --- .travis.yml | 14 ++++++++++++++ Makefile | 3 +-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ad1d12d9..bd806cb8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,20 @@ matrix: after_success: - valgrind --error-exitcode=1 --leak-check=full test/json_unit + # cppcheck + + - os: linux + compiler: gcc + env: + - COMPILER=g++-4.9 + - SPECIAL=cppcheck + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: [g++-4.9, cppcheck] + after_success: + - make cppcheck + # Coveralls (http://gronlier.fr/blog/2015/01/adding-code-coverage-to-your-c-project/) - os: linux diff --git a/Makefile b/Makefile index f08d5cf3..b53d8d3d 100644 --- a/Makefile +++ b/Makefile @@ -64,8 +64,7 @@ fuzz: test/src/fuzz.cpp src/json.hpp # call cppcheck on the main header file cppcheck: - cppcheck --enable=all --inconclusive --std=c++11 src/json.hpp - + cppcheck --enable=warning --inconclusive --force --std=c++11 src/json.hpp --error-exitcode=1 ########################################################################## # maintainer targets From 0cf7ebaa57f83935398f466907e322062c167a4b Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 18 Aug 2016 18:43:27 +0200 Subject: [PATCH 42/46] mentioning the CII --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 73a03363..3015e860 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ There are myriads of [JSON](http://json.org) libraries out there, and each may e - **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/src/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings. -- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/blob/master/test/src/unit.cpp) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) that there are no memory leaks. +- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/blob/master/test/src/unit.cpp) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) that there are no memory leaks. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289). Other aspects were not so important to us: From 585a39a2357dc54ae10afb6f948755dcf6395e6b Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 21 Aug 2016 14:39:54 +0200 Subject: [PATCH 43/46] improved branch coverage --- test/src/unit-allocator.cpp | 150 +++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 4 deletions(-) diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index dcf8aa35..948446b9 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -28,13 +28,14 @@ SOFTWARE. #include "catch.hpp" +#define private public #include "json.hpp" using nlohmann::json; // special test case to check if memory is leaked if constructor throws template -struct my_allocator : std::allocator +struct bad_allocator : std::allocator { template void construct(T*, Args&& ...) @@ -48,16 +49,157 @@ TEST_CASE("bad_alloc") SECTION("bad_alloc") { // create JSON type using the throwing allocator - using my_json = nlohmann::basic_json; + bad_allocator>; // creating an object should throw - CHECK_THROWS_AS(my_json j(my_json::value_t::object), std::bad_alloc); + CHECK_THROWS_AS(bad_json j(bad_json::value_t::object), std::bad_alloc); + } +} + +bool next_allocation_fails = false; + +template +struct my_allocator : std::allocator +{ + template + void construct(T* p, Args&& ... args) + { + if (next_allocation_fails) + { + throw std::bad_alloc(); + } + else + { + ::new(reinterpret_cast(p)) T(std::forward(args)...); + } + } +}; + +TEST_CASE("controlled bad_alloc") +{ + // create JSON type using the throwing allocator + using my_json = nlohmann::basic_json; + + SECTION("class json_value") + { + SECTION("json_value(value_t)") + { + SECTION("object") + { + next_allocation_fails = false; + auto t = my_json::value_t::object; + CHECK_NOTHROW(my_json::json_value j(t)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); + next_allocation_fails = false; + } + SECTION("array") + { + next_allocation_fails = false; + auto t = my_json::value_t::array; + CHECK_NOTHROW(my_json::json_value j(t)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); + next_allocation_fails = false; + } + SECTION("string") + { + next_allocation_fails = false; + auto t = my_json::value_t::string; + CHECK_NOTHROW(my_json::json_value j(t)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); + next_allocation_fails = false; + } + } + + SECTION("json_value(const string_t&)") + { + next_allocation_fails = false; + my_json::string_t v("foo"); + CHECK_NOTHROW(my_json::json_value j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); + next_allocation_fails = false; + } + + /* + SECTION("json_value(const object_t&)") + { + next_allocation_fails = false; + my_json::object_t v {{"foo", "bar"}}; + CHECK_NOTHROW(my_json::json_value j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); + next_allocation_fails = false; + } + */ + /* + SECTION("json_value(const array_t&)") + { + next_allocation_fails = false; + my_json::array_t v = {"foo", "bar", "baz"}; + CHECK_NOTHROW(my_json::json_value j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); + next_allocation_fails = false; + } + */ + } + + SECTION("class basic_json") + { + SECTION("basic_json(const CompatibleObjectType&)") + { + next_allocation_fails = false; + std::map v {{"foo", "bar"}}; + CHECK_NOTHROW(my_json j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json j(v), std::bad_alloc); + next_allocation_fails = false; + } + + SECTION("basic_json(const CompatibleArrayType&)") + { + next_allocation_fails = false; + std::vector v {"foo", "bar", "baz"}; + CHECK_NOTHROW(my_json j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json j(v), std::bad_alloc); + next_allocation_fails = false; + } + + SECTION("basic_json(const typename string_t::value_type*)") + { + next_allocation_fails = false; + CHECK_NOTHROW(my_json v("foo")); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json v("foo"), std::bad_alloc); + next_allocation_fails = false; + } + + SECTION("basic_json(const typename string_t::value_type*)") + { + next_allocation_fails = false; + std::string s("foo"); + CHECK_NOTHROW(my_json v(s)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json v(s), std::bad_alloc); + next_allocation_fails = false; + } } } From aa7f5ad8b123177067fb0b943866425d22e20013 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 21 Aug 2016 21:48:15 +0200 Subject: [PATCH 44/46] minor changes --- test/src/unit-allocator.cpp | 93 ++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 32 deletions(-) diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index 948446b9..9ad162c1 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -63,7 +63,9 @@ TEST_CASE("bad_alloc") } } -bool next_allocation_fails = false; +bool next_construct_fails = false; +bool next_destroy_fails = false; +bool next_deallocate_fails = false; template struct my_allocator : std::allocator @@ -71,8 +73,9 @@ struct my_allocator : std::allocator template void construct(T* p, Args&& ... args) { - if (next_allocation_fails) + if (next_construct_fails) { + next_construct_fails = false; throw std::bad_alloc(); } else @@ -80,6 +83,32 @@ struct my_allocator : std::allocator ::new(reinterpret_cast(p)) T(std::forward(args)...); } } + + void deallocate(T* p, std::size_t n) + { + if (next_deallocate_fails) + { + next_deallocate_fails = false; + throw std::bad_alloc(); + } + else + { + std::allocator::deallocate(p, n); + } + } + + void destroy(T* p) + { + if (next_destroy_fails) + { + next_destroy_fails = false; + throw std::bad_alloc(); + } + else + { + p->~T(); + } + } }; TEST_CASE("controlled bad_alloc") @@ -100,63 +129,63 @@ TEST_CASE("controlled bad_alloc") { SECTION("object") { - next_allocation_fails = false; + next_construct_fails = false; auto t = my_json::value_t::object; CHECK_NOTHROW(my_json::json_value j(t)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("array") { - next_allocation_fails = false; + next_construct_fails = false; auto t = my_json::value_t::array; CHECK_NOTHROW(my_json::json_value j(t)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("string") { - next_allocation_fails = false; + next_construct_fails = false; auto t = my_json::value_t::string; CHECK_NOTHROW(my_json::json_value j(t)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } } SECTION("json_value(const string_t&)") { - next_allocation_fails = false; + next_construct_fails = false; my_json::string_t v("foo"); CHECK_NOTHROW(my_json::json_value j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } /* SECTION("json_value(const object_t&)") { - next_allocation_fails = false; + next_construct_fails = false; my_json::object_t v {{"foo", "bar"}}; CHECK_NOTHROW(my_json::json_value j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } */ /* SECTION("json_value(const array_t&)") { - next_allocation_fails = false; + next_construct_fails = false; my_json::array_t v = {"foo", "bar", "baz"}; CHECK_NOTHROW(my_json::json_value j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } */ } @@ -165,41 +194,41 @@ TEST_CASE("controlled bad_alloc") { SECTION("basic_json(const CompatibleObjectType&)") { - next_allocation_fails = false; + next_construct_fails = false; std::map v {{"foo", "bar"}}; CHECK_NOTHROW(my_json j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("basic_json(const CompatibleArrayType&)") { - next_allocation_fails = false; + next_construct_fails = false; std::vector v {"foo", "bar", "baz"}; CHECK_NOTHROW(my_json j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("basic_json(const typename string_t::value_type*)") { - next_allocation_fails = false; + next_construct_fails = false; CHECK_NOTHROW(my_json v("foo")); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json v("foo"), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("basic_json(const typename string_t::value_type*)") { - next_allocation_fails = false; + next_construct_fails = false; std::string s("foo"); CHECK_NOTHROW(my_json v(s)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json v(s), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } } } From 94331a355d5fc2032810af7160504addcf86f783 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 21 Aug 2016 21:50:13 +0200 Subject: [PATCH 45/46] removed LCOV_EXCL_LINE --- src/json.hpp | 22 +++++++++++----------- src/json.hpp.re2c | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index a8289a49..b5e24206 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -7734,7 +7734,7 @@ class basic_json }; if ((m_limit - m_cursor) < 5) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yybm[0 + yych] & 32) @@ -7868,7 +7868,7 @@ basic_json_parser_6: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yybm[0 + yych] & 32) @@ -7938,7 +7938,7 @@ basic_json_parser_15: m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yybm[0 + yych] & 64) @@ -8031,7 +8031,7 @@ basic_json_parser_31: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; basic_json_parser_32: @@ -8068,7 +8068,7 @@ basic_json_parser_36: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= 'e') @@ -8212,7 +8212,7 @@ basic_json_parser_43: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '@') @@ -8248,7 +8248,7 @@ basic_json_parser_44: m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= 'D') @@ -8289,7 +8289,7 @@ basic_json_parser_47: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '/') @@ -8331,7 +8331,7 @@ basic_json_parser_54: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '@') @@ -8385,7 +8385,7 @@ basic_json_parser_60: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '@') @@ -8426,7 +8426,7 @@ basic_json_parser_63: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '@') diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index ffa20f39..fdc00638 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -7698,7 +7698,7 @@ class basic_json re2c:define:YYCURSOR = m_cursor; re2c:define:YYLIMIT = m_limit; re2c:define:YYMARKER = m_marker; - re2c:define:YYFILL = "yyfill(); // LCOV_EXCL_LINE"; + re2c:define:YYFILL = "yyfill()"; re2c:yyfill:parameter = 0; re2c:indent:string = " "; re2c:indent:top = 1; From 1e896eb91ef940cda23a4bb49a302ed227c442d7 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 21 Aug 2016 22:38:56 +0200 Subject: [PATCH 46/46] improved code coverage --- test/src/unit-class_parser.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index fe005503..259daf84 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -445,6 +445,10 @@ TEST_CASE("parser class") CHECK_THROWS_AS(json::parser("\"\\u0\"").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("\"\\u01\"").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("\"\\u012\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u0").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u01").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u012").parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser("\"").parse(), "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\\"").parse(), @@ -457,6 +461,14 @@ TEST_CASE("parser class") "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u012\"").parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u0").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u01").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u012").parse(), + "parse error - unexpected '\"'"); // invalid escapes for (int c = 1; c < 128; ++c)