pax_global_header00006660000000000000000000000064143761747740014536gustar00rootroot0000000000000052 comment=927088ffadce153306a36cef4d0fed52609208f6 alcotest-1.7.0/000077500000000000000000000000001437617477400133615ustar00rootroot00000000000000alcotest-1.7.0/.github/000077500000000000000000000000001437617477400147215ustar00rootroot00000000000000alcotest-1.7.0/.github/workflows/000077500000000000000000000000001437617477400167565ustar00rootroot00000000000000alcotest-1.7.0/.github/workflows/build.yml000066400000000000000000000055261437617477400206100ustar00rootroot00000000000000name: build on: pull_request: push: schedule: # Prime the caches every Monday - cron: 0 1 * * MON jobs: build: strategy: fail-fast: false matrix: os: - ubuntu-latest packages: [ '.' ] runtest: - true ocaml-compiler: - 4.12.x - 4.13.x - 4.14.x include: - os: macos-latest ocaml-compiler: 4.14.x packages: '.' runtest: true - os: windows-latest ocaml-compiler: 4.14.x packages: 'alcotest alcotest-js alcotest-lwt alcotest-mirage' opam-local-packages: 'alcotest.opam alcotest-js.opam alcotest-lwt.opam alcotest-mirage.opam' runtest: false - os: ubuntu-latest ocaml-compiler: 4.08.x packages: 'alcotest alcotest-js alcotest-lwt alcotest-mirage' opam-local-packages: 'alcotest.opam alcotest-js.opam alcotest-lwt.opam alcotest-mirage.opam' runtest: false - os: ubuntu-latest ocaml-compiler: 4.09.x packages: 'alcotest alcotest-js alcotest-lwt alcotest-mirage' opam-local-packages: 'alcotest.opam alcotest-js.opam alcotest-lwt.opam alcotest-mirage.opam' runtest: false - os: ubuntu-latest ocaml-compiler: 4.10.x packages: 'alcotest alcotest-js alcotest-lwt alcotest-mirage' opam-local-packages: 'alcotest.opam alcotest-js.opam alcotest-lwt.opam alcotest-mirage.opam' runtest: false - os: ubuntu-latest ocaml-compiler: 4.11.x packages: 'alcotest alcotest-js alcotest-lwt alcotest-mirage' opam-local-packages: 'alcotest.opam alcotest-js.opam alcotest-lwt.opam alcotest-mirage.opam' runtest: false runs-on: ${{ matrix.os }} steps: - name: Set git to use LF run: | git config --global core.autocrlf false git config --global core.eol lf - name: Checkout code uses: actions/checkout@v3 - name: Use Node.js 16.x uses: actions/setup-node@v3 with: node-version: 16.x - name: Use OCaml ${{ matrix.ocaml-compiler }} uses: ocaml/setup-ocaml@v2 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} opam-local-packages: $${ matrix.opam-local-packages }} opam-depext-flags: --with-test - run: | opam pin add alcotest-async.dev ./ -n opam pin add alcotest-js.dev ./ -n opam pin add alcotest-lwt.dev ./ -n opam pin add alcotest-mirage.dev ./ -n opam pin add alcotest.dev ./ -n - run: opam install ${{ matrix.packages }} --with-test --deps-only - if: ${{ matrix.runtest }} run: opam exec -- dune build @install @check @runtest @runtest-js alcotest-1.7.0/.github/workflows/changelog-check.yml000066400000000000000000000007341437617477400225070ustar00rootroot00000000000000name: Changelog check on: pull_request: branches: [ main ] types: [ opened, synchronize, reopened, labeled, unlabeled ] jobs: build: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v1 - name: git diff if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-changelog-needed') }} env: BASE_REF: ${{ github.event.pull_request.base.ref }} run: | ! git diff --exit-code origin/$BASE_REF -- CHANGES.md alcotest-1.7.0/.gitignore000066400000000000000000000001051437617477400153450ustar00rootroot00000000000000_build *~ \.\#* \#*# *.native *.byte *.install .merlin _tests/ _opam alcotest-1.7.0/.meta/000077500000000000000000000000001437617477400143655ustar00rootroot00000000000000alcotest-1.7.0/.meta/alcotest-logo.png000066400000000000000000000125251437617477400176540ustar00rootroot00000000000000PNG  IHDR;SgAMA a cHRMz&u0`:pQ<bKGD pHYs  w5IDATxiU\jd_,T ,Evqڱ]zڞ=gteePqDvaDPdE(23͏IeFnKUR9qEˈ/njJ)E}>Z[QN7Ϻd\_e4ۛvppCye/TcJ<\ƎJ9-b_}\ߣC 77\tŵ C\m&n\:%F{. y<@tG i1HIY9?"͌:%|k/Mu;88tn'_ZGG۶0 4ߗuppdJyyr ~@0M93;6+4 =ty9ƺ5+skJ)e3ks\_C3`P]5jǎ0ppoXb 2;\_CY:888%.aos}=9];>r}=9q>| 褨̈.\7Ar u(I@ 0s}顔 ˵C L++흻x:tcR!dLKNm;88ģKB?0soK˜ͅ ;n/]!t!`hSKw>vq #w}$\/ùj/ԅMdtsnTɤɈv9Vpipei™tG $cl\Zұv34uWqr:ww +aRc (6s:ev?VFPW+aRݝ)ћyL #;oh_:j ۡ )?&OyT.رk*)[nmPAFkB;*;AK[v5TOЌ`[N[[K M}4v6 ѩA ڦ~9[s4BiUvp#` 0"Z: ESj(ʏ <+y=9m*.jv4KҖlbK]%f4C"0Eo~|/uu9aZ swxaI;1 V}q]g|7*hA]ݔ։ %ֶeJ vU|arMaL2dUC0Ny׃w.Y L{ iy}#_1v5 ࿘(uWksз()`,@ zѨ ])pw۟懪2:R^WppރfKw `@XO/l'!rXk'Q20OcPeOPC=kLYnqkOAad|5~}zhMCКd !k5W_Sj_\P}wxƅcV~^!ÌQ2m8P%Ha`B@.п8Q`yGO-gޗiVMᣯǫ$9].K:^p}` Я*a@Q6x8SQpao\,v}lϥ`che 毆?}/{"/&΍1S3Y2Ew5$ Lu:TyPHQDY ͇NCtUp*gv } %g=H۷ α<}m{ ;ge'\89׭e j ZU8Fv*"n!)o؝\ "@t^R|)p`qXQفC,?( RR=\~ɦ[gO=fOKr;,ZYvT pJdM vBhܟyU2Y@,on}Se|닽dcX\MGSԢyo a'i.'o]lcX,Z߷ V kj"4I{q)E B^_;Y [C"D2GU&fSu M S4Xl ;=+`Xr1r_ہtjK"%2tYY[N[ bhBV"ok` ;P [d-Y/1/6e/+d$D 7n]ob H0j0=͉Gpr tHGiLgin6\vj&Tp Oa޲pVl)BiJ3@[xf9*pROe]ܻ82i젬Zsr ̰Ph6ض730j|#ٔˆO-b9)GY'KVpّm=s۰^}1.JThA+ohC AK6]Y 6q@ez.CNAKFdyDc#ZR nt>.71R=H׫eVm_< zPuh!ڻCg$׬&ʈ*zw'jAeV 4:Se4A;z8~'EwDF$eSWۯΛYpuXl9'ˣEkEΌTw MbpYll~jOV1b0["egOjcG'ci-v~n]/27_$ moٲ<3/UzudAaxnohZUiUBž kA4(Txa x6|ۡo)T[4\Xg1wA ܟv_濪H)oNu@ x_!|PE&xԂd }-|!74pf$1Pg hSdT>\V=uؔ& HLL|xAU9B`Q FC|M_QDz%/zK|O 8wb6*JC&·]/!x&GbI4u6Z; rLk8\ gTopwUvh ўү~u! {o `wuE7є6(աfJf%"cr-wۻGM'*}7z(˞y\?^3c0Rs#}O->pNO޹ky34I,cԦvGP CDU\s a^xJ2"m9Yܑ3^Ն\ynlImtPȔ`+<$/zYUw>kfwTYqTy,4n6A/..E$ T`R7%6D]X5X!ijwlj#,aH@#מ m?xI, @Tevin~.}am, GI Dbz ")}-^?7 >Z=%5{10w<0x>rk}i)Ԅ^4J.3IcE|!\.OG]CN5b$vbLK[~!:O~(70VTݘ#!\$Jip pi0r`G"pkPy`bx= AYS+kk*RZ'm}Je)`Ľp$4 ɥInKK`@vGVJi<,۸G¹ucYnMfWG{aWF5}ljgUa]ܺ.Ҡg!*fo$4]4PT@@|,*U%Fl_H'|hiCkm5Lb-ެ]l mm~9dxU4yLG dd R0Q8ZdW,[Lu6J* Z'W~Q63\7 g>!$t&P7J 5 ʘZ>\.\_CCŪ)%9aMYUU}=v<-?zp٤:KUdyQIIY9>|DR!< =)F988t+<=ACGGLz:ϼupp4JyL[ IQ*{OU?&bS;1 aK Ģ f* x%4x$'XtEXtCopyrightCC0 Public Domain Dedication http://creativecommons.org/publicdomain/zero/1.0/%tEXtdate:create2015-05-30T17:07:15+02:00je%tEXtdate:modify2015-05-30T17:07:15+02:00htEXtSoftwarewww.inkscape.org<IENDB`alcotest-1.7.0/.meta/alcotest-logo.svg000066400000000000000000000357201437617477400176710ustar00rootroot00000000000000 image/svg+xmlalcotest-1.7.0/.meta/error.png000066400000000000000000004337441437617477400162430ustar00rootroot00000000000000PNG  IHDR6|}sBITO IDATx^xEqZhI ދ*IDzSAJ*"*( {zB۰Y.K9})-{ov&~w     #8RD@@@@ Dy    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9    8L@@@@ D9#кUGw(JObbFh8z6s>e" ą!ڸPƍj+ݽwR''<(.$(>|voe" đ!8mƍU\!sqpC s3j .>vd]k 8C~ֹ`w D@@bP _BqK,8=zgoJ/̚%S}|RiӬYPbʠdgh;(@@ ${oCK14U؉߆.G'EZ 4r&?tŨы JN4cժX*-^f/6/ܽMca̱Ю4o,f|S[<=6ɸrlº3Jy ԪWz޼RHkn֩*V}gH$Ibm9gѭ1j۠0L|Q_ նMsٳeՍѾoa~}wtի%t@pѦIs7ۘ{uak@ΜƌXp~m)Rx+w7opAJ*`8s06g7'/Vȁ.\qv 6w`.[q>ؽ   [ 8EvN.'_~b탠nL@ V$ۃBܕ~!@lԉ{(%2fL j|K,[si IĻEd42ic45Ufl;lظ/^4T{Hwu+ΨtԮUU{BBUܺugVgmNIZN3@@p~*L^Ij"IǖyBpF k+-ОbUvK,Rϋ~o#GNlw~Y@#!ԫW}͚ͱbcA>vuHV3HBRSھ'Zr+ ''  o@gV/9e.;OgkA'`hy]9Lj J- )+zɀcB_]J\rōC\+`'쓧: nG;6@@po'E.gU_51$_,)jK+GW/O< Xu[¥9+mwM'lϙi|ᔍ2IແgQAS&|xc&Iϒ~iλ_}u۠ML3eL%S%Δ,OАA/>ށA<|| =b\{aP|\Ѹ)V8+ZH~[e\ru@YL,_[nf+ڠ[S -SBBKF?'Nϝi VlJگIʖ̖-D儔{d?$6MD[LP]SժV1}%H1mt7؆A{m\J>xp}-[6R|LҾwJQK:}1e +/Q|Qָèlٲkۼtbӧ t'Dއ(}7s7XY,GjfVyӻP*Ӏo~r2eJ/Сc29Ͼ5sdvרQY^?=qyY/w9'|/'TT X)!ZMB6Gzߝuj CbIJ,_H1ZHTI++L>/<8KR*Ax "8AJ&Q0S2ˎ~2Fz%WDܶaƠϮ 8xlOnִ|VKJ$}{ j]xe1O79 324ZjV޴yq{<<<ƌgʐaCVA[L3:v̠̙ٝ.]RUr2,YaYФIݡ$MQ/G3[:2ᓃANj*Il…櫶AO>g7n)!Z__lVA4%12èͭ-SfԕW\Pʵ9tcOM6^Yntܩv7-H/$ѢE#`7oޱJhǕet'9'-opllݪmտ\jӺ1S׮59Dǎ(W fP~!OXra#0nҸ%z*[y?~"Sq+F&N1C:yqټe!ttNN]ݔ/+U,#]oNlJÖtr^E@@&:0賷Gn1jBq!U$Δ5Idړ_eΙdq3N%,.۷k=Dٍ jABT2Tv-2VTZܱCʯA2{BQ:Jܺkv_N;#d< ׭rƭINV?(]٣L#`WVLq,߽[G oz톼ȕ3{f*/.Rwr=PRR>|4o0L۫t'̌#a.$&@@gpm]_ټ> ʥɻ.~rؙV—2mR﫫/.v;DѫW?|;ҩ;戃pKyy,@.Ed ˌU"a }<$d`em{gI+_Ѕg 5+\]^ipsD ;=U͚7PʑN;V#K"1Ν[MA-]%i>IY֭S}\>Ged+7.mFc$F<;wQFV ϊ˖0mTJ|oWd<.kG[yRGAGܸߗ?GNk ngWu ;mwmA̹rG`k,!F2T%˚1prmʜ2Ȑ}!kIoGd?/1c%SLUĔ>#Q?'NY| Ssުe?f9it++F]7]I;t̠2eJKjCB;uTiDƓt?$J,O/t$LkJM%HUϪ$n{ciO*S;Cs^rDw읙g^ڵydXJQ33uX2bdu_NPoBV~ձΝd< G%@,X^vTiI`qKbCQߗX^;<qDmI[2T v0,ij53 v6ѣ˗פI3YmS唖R|IK:<_Ye#'kJKdSt ]ŨdhǕe̼==uF5Yy2vW%fm]YI$hjk||*j<=dNagy/gD=xxV^?~SZrҠ@@ "hEڦQNj ~ dC 7[_cX[Q.2VIue}' cǺٞx2‹CK+!8:⑃hϘnjxG˯+xM8cԧۛ-%/9w3Տ%˧+"}ig?<9jTYD 2WJN wﶷT騉81jZ$qmByމTlwH0FKSKMoz׏0y{? |Ulܖ y3%# 5E^TdL: 1}gUDc2NmLġNO,sf"WV ~O0qYjʯY$3VL?[<~qeSٴyTI[(>nuJ0Iztd'~uR|-[#P`|.2̡j-n&~Sjt{  4Dp y5G-m {\zNڳ4Q$ʎ4Sfv٦ Lt鹲)ODjDiF%CйQb<.{((s%N9A][RC~<8|2&Eн|mmP*N:[$*4dklJ֭;j?^YJ&NU~&`|ڿ_58mCP2ﴣ,7l_YiF̦-N>gg Oz3cWVl- r0\U[ťףG VLGZ~ɋW,i0}`ڳ"V8ە%s(S낟vԯ;uD"V7~Sr6";  g8Aex|x_!Y$ح Gp.mOm {/L?(UDl Yx s ,2nJ<<d=3 _D-uqgʌl=r@zY8ڠ~XW8Yt` #w^O;hYw`iz0wі`*d7T %珍6USegVۯ,O?ʭmɳQ_SZ>F-fhǕe5;9iucbcGiZWU]M~gWRK3 3?mØw^^^o1Q#KO4oV_^A}ޕ+.^rԹ߸q+($Xߠ~M:f~v3Udd%<_/\nߡ[qii]N$G; " $!BYF_6㞿 wv3e&㭤#'~etPzɓ'X|tفUl鲸ܹ kގ+[B*GPX5C)Vʓ' WF=h-ͳΡzJFO2-U]FINv/u0ve94:lrq Ē3h}x+Шy F=mydeן?%w*6,( jg_E3,2OgQ)Upɓ'TA~^e`jŸns.i ,3zaP%o3F`PW]֒.ܵߥK댛i`2Ri@Y8={VYLؤ x4ʲ.wt;S]Y}b|rjx/G@@1DHɍY=5{[x9%`Սc"~vh*~}إ V8.ÇU_M+ctY?n̄`GG=,cKR8'-Q$OXhW V.78ɕes<^4C@ 8c8>Ջ-34<{*|arqtkza3˻E˺.5'k1)?gR'NڱSoS˪ކןx {ZSƵʀӧK$:~عkc5D[N5uU_(Li}di_V'y]>w c+m}`=W71r-78~e0wM#xeY~ts:96J|bx2%]s;rwl_Yډq-$w9KQ^.F]&3  @GݪCvQ~VYW3fL_TqVIŎΝt~Q7G=,]Oc㜴vS98M鲢R7Lnv "DÆ~&SR(+V7n6ŊsWyh9mf⬎8 k:grB%עE#RPmҧO[Nr/Nd=J #Ҽ>S }W oܼчJÆ|DUQV~HGϐ!rĮZ<`4q˿#?߲{<~^|ي 8@BTlq-WH,b\v#!Q3e{BzK1"*6(հg!%Jħf̓&ywY29ߛb0g$M~)!<0,~IgU(rxɋDlM#e 5;ީZf4HTs"SSY.3]x{ ה 9ZlnKʲu_2[B jvyNf &˔-O>F… t^"7WfhO Zʕ+!e)? ,_J2xeI-ҋw_+d`XxXĉd&X:s#'ÊsWQP(Ev9'˗7Y'm3w@]<礶yVնB҅?%2hт1Bm6p̅u)S_[ ZjڹS+sJrm~;'BTzꖠ;G]m|}}<~"?-SRn]i\CM4j.WVP`j׫)1Yy#˗+9jT29sS/*1<}Eg0"%e1UήkI&M5*O8(ƲCk)+5|5I={t6SDgRYd\~HDM iFd m>Dۏל4aI)q4 q*a4bԔOfL'?KjW~~V,azƛ)_N`,i3ƿgbXZQ@{^x|dG=,WjK(;~Ni0b 礚6$.V\]Sݑ;| [%[Dul+9 JBTN.3S-wZۏpg͛ dٙ_?~e_MSz%µ)rZ{ف,OԬ|9[x 5w/KAܣ{ntV$\U6罜n@p`ɵ Y9${q'H wߡK 73³ mu⳦4=<$r?kj_YJ%~K~Yfr^2|$j:2*tyS<._:Je`ue[ڠi|n0՝VUFJJ Yw7%l !խ3um|ֺ^jU#GNر/Yv^(6vQ>+e)8egy>|Խ K"qexe^}(B3wXFO;ȕ%&]YƘ1J9u3[}VljvSA䩳@{L>~{UĐyIGnj;^ZRYohycfe .m;<ԩsSs{@n޲@rƭn,z/mOɀ 8\XѸl=52'mŧz$9dDmxx&cQ '.#ɓg_,m<+zQVuhR4QITɖXv-?h,Cdɔ,yϞZ|c+ǎjܴ \(S[a%g 56'fg˚YW{ M;s[qNJ7mpe9Rae˔PZr#M|pcD@@NBoq@u\eF`{ pF驸mǘ֭Ӡn{  8\ND@U5,gΜwdkW7wϚ9Ƌ3g67N|\+B@p uD#@@~iVbd|Ze)3y؄  q)DqM]  #nE|֑@@`=@@wx;whX@@@@p9B.wh0    !Z9@@@@\N2    #@}%=A@@@@ Dr#    ucIO@@@@@Ѻ!     >hX@@@@p9B.wh0    !Z9@@@@\N2    #@}%=A@@@@ Dr#    ucIO@@@@@Ѻ!     >hX@@@@p9B.wh0    !Z9@@@@\N2    #@}%=A@@@@ Dr#    ucIO@@@@@Ѻ!     >hX@@@@p9B.wh0    !Z9@@@@\N2    #@}%=A@@@@ Dr#    ucIO@@@@@Ѻ!     >hX@@@@p9B.wh0    !Z9@@@@\N2    #@}%=A@@@@ Dr#    ucIO@@@@@Ѻ!     >hX@@@@p9B.wh0    !Z9@@@@\N2    #@}%=A@@@@ Dr#    ucIO@@@@@Ѻ!     >ߦ/ IDAThX@@@@p9B.wh0    !Z9@@@@\N2    #@}%=A@@@@ Dr#    ucIO@@@@@Ѻ!     >hX@@@@p9B.wh0    @" =q_&MO yO>s߾3@@@@KkU*-KO<]v/fF' ;ydRX"?>=)@@@@IJMY|S,T|Ǐ4i…߼usoЃΜPvgΜ1y{x{OO@@@@X)Fі,Yt)]eq{nt>JE6ϐ.IsdbE9.Ν|1KJ/:oq̹5k6jgϲfjkסg҅׬QyjۗS1ӣ-aI+Wm0M     \  .ݸy[mhxx]@ŋ-eR g(@@@@pQqiW/(UN;ul)\m9pఫu>4L2TXFϘ1]{`)@@@@G8Eԩsmv3,wӔK_q/֭;&Z9;i*a{ t@@@@\)BϞ=?rRoݾx/R@@@@@W`.ZW9RW#+W     EZ5[̍)YX}|?xxm޶m|d\R̻)^p~r^r}ν?/= ILì/^FuV,S5   he!A{7kZOsJ)qۆ j]xe1O7ut4;tpIdmTgƭÆO 2UnTs.63uͦ*cJN͐!C jԯWc?&OejGJ8eϞE*锼$A8yƍ7;UpD~1ZjN +TXN[7R7p1CIIeeye̘^ǽz~8jvc)I$1}lՒSL!vha7onP& =^&t?N(KnA|֠R;o/{tf2j)d_*Ϡ{qn| gڴfM!ageJ(vꗣ*U,Mgɦr1 cBv9   )nhYhAXܹsow w[S2BEJeeDrOװaGQq"IT2Tv-7nR!2̰cӧ+nP5C?K~Y=$ھ] %W!oܼmp`z$6iRI:v2?,ܸq;i$E ڵCw(߾࿶(qp-0ޤ(V-*rRM8ai4}QKInjpa]Ӭy3;?v̠[RZ%ʕ˙`) ׅ.    n)V!,Y2 .w[JɫOoܹijkǴaq_&{2t䧅K9?U"|N;_nijNUe[TgΜO(Qy>hHϪݑI0W κui;UėB$)[%Z*{٨dx[]bŲko޸KЦu3rAT:%-_rxxxt=`ǎNUV]c!D#:2#   UV?R~6`rf~?n!̙3x[t\e%L72Wjb3vU%B2!ښ5S2YQj k6|Ң#VRT1 ٫;Ha/5$$DIhd?˜9(…ITujQ?ș3Kժ8IM'N;n{H_' Ss*TKlI('LєJ)!b jr|w֭Ԝ #Fd!,#:2#   Uxdt\b?e&@͙ aϟ-A屮LmuG/Y2\*3(>vn ?w,;2Š!f5>&5k7uIMɑ3 ]7EPKK,j\$^v]EP{Y(CV Νxɲ.S͓'sQ=g< ^ZrQd{@@@pKZ.L|@-F[S'_%d$eRSMtI:IGUUR?mjwI?yR?nf8t╵k77 mn_e /8yv?3tmZd;_v?%͞* @@@@@p]&O>]d!#@!CZy-[BIe]}^H!'дiz8pzYK9^ Je^%ʫC2vykl= %{2    vpLYlf^o6mKV\>g2 Of}=qe'4ޝ'~};1_Ϛsߝ:Xt7Uж-ct-?/' @@@@R=CVo3v-2P"kZ왳fɔ%K,Y4EeڴiviYʖp߰0U 9ě5ձ]˕3gvU Κm;e -Z41n#'>|,&-ȠڬY3g̘.Ct9sfU|U '՞%M|@@@@'pmxY2u*N\>}^^D?Z}}jƣu||R;CbMeq0%cM[n- 3sRig*LC6C(^p ȣ&~C5D.`0w} 衚 -]V5q8A|AJ6M~J@@@@\@?⢝~$2K]W窫*͛[FR]*'J 5ӌf9dSRz%йyvfx$ fPS aT~)M2\z\ƻK΃wO<2X[)Kxib)Sh q]V<{BҤM[TjT$"   pݻn۸uCJ I#I =[LL2SHܹnrԩ"!!!ʙ.n+/jE\՗k@7{y{G} &Y^,[,)nm;wXrnTU+UJEXML>b? Q"z V(@@@MBQ|eX4֔uDX̻+qbmN[7X^}zljwI׆0.YHHZN.3VVKS-KMH5#v={ c   č[hoݺ˯+ڷk!vXKaޒ;޾s/yd3eR|VM==+3`ee׷o  _MbvK|U{xe-}}#]OGd(eڡȤ'N ~JgBCʑ]atY4Hi7oݹw7Vu Ι3fL?b̚ kY|TPY2WN ȑUN 9 faveuV΅ Օ$GN>/on%R;wj%/S+˖fXF-ZPMM,7_~LQA{iwZ)Ӿ>iV/Qp|I gG@@@@ .*D+pӾ/;,~~.Ÿ[o`|9 z,H EF]_9%*2qOʈTUb4b/^xO 2|9m΂+F[^L8Lw_[9f1+62_g& 滐d< v.Z.z;r9ږ&U=-D7q/˫UhfN=q}2rE;)}!@@@p6Vpe.iѣ#&Mbno2Qwx䙏}uwQl䉻vSAo?yĚVF߰C^%SB Fv~IW!0l턉3LmME7EkGr8q ]նkС7f<-\J[޻ߢe{1ޤHnҙ&{4ޟ1nvٴyvA6nK   ı>JE7i\Tb̚*>}~Ǯ}7n3?N"eiFK*-[f/o/@2O<+᡿ccޒI7yR()񑶮'Y /yD?ڵts:$Q2Lw7ShԩST2mY^6OedM,GK/_ߖ25+dZd9eJY6C^$^xYDXnL`%ٽ~Ŋ%Rx{}Lk:{S[.`V(_J&b.VLAzu.Z"Wep,'W'L%!    lsSD{\E@땥/kҬ4v"    <6сȺ}K<˖)tSVEsA@@@@bCmlژ$Jtx5'    C 碵 eD ̙3#gyr7kZ7[,J{b@@@@BFWX!39d-&&@@@@0#Dfpmڴh@@@@L0 &={~݃^豓&r     N12 @@@@@Lt`oQC@@@@, Dk1@@@@@{ (!          [@@@@@ZLEF@@@@@h-Jy     h-"#     `oB<@@@@@bBS@@@@@"{Hy @ +[ÁA[#   )@6z8KVUNZ6|3g.t6*iPHXX؋/>||#GO<s;wy2V)[;=2$HPBO IDATJKʎ.][hBۚ}?6Tm{w95UEϖ5OdIC={qƭg.!n\D{tk"E,̙$$$DvSі`wD]E˙#_ӧϯ]y̅o^YR`{ ھY$sȑ%_d֭g]?BCCxlVmK·JKV\VW^x(_9G~*X7%hu9i{8'9.4K=@.L_1e7.~칩6~>!D s;o9\vӒ %Ksgɜ!E I ɓg׮:q/#K [6~ٳN t ﱰƊ3ʸk%-Tn%}-2C  X.!Z˛9{ )S(UfP#N( シ<%U$yID>MS/eE=;V&M kO͖(Q:ߓhVKN$g{(6/^P Wr}[%)]j2+Fy͛J2+Wmh:{nuIWqqV]ݜcT3SK$HJ!7lF|0_[?Aԟ$g QGa0B^nf]bӣGM HĹn mo)\(fܯ ?pDFc;~_V_rIQ@µ Gf|6e79ϝڱc[.oN*6gQLz=)XF]#IGAD\$ɓO7@Ƚ[4U8<`}-zY|/y's&PM&ge/ ؖCx&}Îa;yO0*zVm Yն2)jE.Rmqy, ORaeaݫ3ިQ[o5;-qmկ:mʘ2'Na1l=va2y \T jՑ;w:t,ixcmM ηLﱞWQ^""eO8JxL  ͩ%/c-ճHJ$1i>y$yխ ;V_b6^%yˌ"?(6j_gR&2VKڵ=X'OݬxGXn]۷mwԱp+6`9i[4؊ʯU.wDу.&_ 0'mÊU2)3.AHƗ#>>-#HZ``'^n |0дcEpJ[;j^ 2'NyRJfelXh?5(_H$@$@$G!Lx"ñ{;bZL!'8,U\\B*©@FFVO 9S-vA\Bo*ᲳnÇO2f.+f\`쳉կlxѣK5U ##l8[~f[G0*THBa>DrW"C'F$%a3jϿ6֮tg MN>EZ-ɉWp76`9iQ ? MDyĠ%Ò?EuaNVg Q]y+cF3?\)dE!Kgu߳urr bXX}~;z-Z/|x<}'.]#vmfXRRCK7Y§/aou"'},gusHHH'`5P7ϒ$@.@_P`ї}:/4$Hb?>Y(g }||(:i0§IY߰C2veFCҷΏ p0,zѿ_ΐa,=xmްqZCR,AUlש C<CpHfv.iNjj|0 ,&[%᫮BQI 0'AEzZ#"te ^z2VR=K MkW%~crxq?*6C|C0 iQL#Sj}Q~vkQsCSϰSHHH&^fkknA??"8W |$";;"f͛:uVʵǜТ`fspT Qk988 #JdrT$8œtL2lBR +2lب{|q9jo,QHJQ\ Tx'@8*,`jXfEXҤDD :@DIePofL9-0?ݢ`X* %Ip9:Xdy<&dc&:hgAU&?$يB5Ne\;&ZDң*$%?\]yy\Ʊ٭}FNyu e2HHj3}kGf͚ <5VV^jV}NJIF *elsfN'_c%۱C~n S1¹TLZX-fͼYexȇg.UG69?VH~6]mx5/arW<gC9qⴡ1TX` +i,R 6ᠧ:gtX)Loƪ8QlF e9؊Eck ə(֘G"4F /}(bբT|IsYAbX4G[& רӧ0R($)jT 4< ⛁1SoFJJr۲Ʊ٭}Fbo\`BB$@$@$ pM-ɇ WYQca8!L!m|Ȑ>-[T햎߄#>/7)/HJԜsӘ!9p܃gFFDlӦE9b.3|h?}Dlww@MBfTVvi>ew Yc1th_i=8,q.>*Z=}7`xD,bzc9?^`͘>ׄΎ6T3N*`쓀tټ9VJɑm3~0C_=, Y$\sمjg(0޽gYS!}12<`R :^_^Yո><ܥೳs e"礢4f2wUa"3#DE#IGf3u׫!4fcFŽxp²XNGef|"ֆu{nV}C+(S`+잰h Ul7/$86F!Aص)Fkbpŗx|SoZnɣk'т،T.DX8@6t}V>"7\֦us=-su {G1B1W |O1P y&@6nkt]"8X 3 `ÎA6Z K%nEhԎκ.ߚ4n.J>} Z\s$8œ mQK)>C'q]rGLpłud(uĢts惞@-,M|9ٓ'/X[Z08(l~!inkT.Q+bG7Y'HHH$iE8 N*9(.](_'-!! uMXZKZtD0A-,mW*ol7|<aaoNE̯&7,tR-&{WQrI V 1FW>"_-DK̖{ j`(SA;f0Z[&.iq"z N}܇)}_T> j`R3 X7'RL|^sTl!`Oܻ D%8œ'R,;a,ۘMh1RE͝ w`l 1DŹO+J(%*[>YK1V:Z*S&,EM|6D;|`Ş-?ҶmKIIX,`yP/zF.7pNڊ`g9|Es +QsRז` l7D<OaE{ .=j>xԱ՜T|P[3={v'b-H&ҷwרxS).z+q,Tzj Q*: oڼẊ1uV+ ׷{df[St>.sv   L5Mvj \1XgPb9X>dH'N'&X`ѨAJE^lx,SR)iu7fu&+fh$+G,=|!sf_)cboN.XsШT"ye'˗sv:اwX&i"@LBxBaU h`E9DĔ] kҁZNe1a]+R~ϔ mU Kj`(ӺKukd>f{,nkؑ8I)WfcRHp9iCZI u}n4+*~uⷫu5iּy8Jb9ic E ={*((2 b]`kxFƴO ]5 N@,3l)x;g&%wiQqp {ȁ( sVV}G墴V&IHHn8hm?ؼ«nѣJ<bA.&(o}O}HD,F+ۡ}IGiD֍:hTS l50w?L"\E(SEH4t>[ڷ??#}G 2\llt|;i%hi]EsR{B.o0`X˒&EA0Y+Z.Xs fz%Yk>td+Y{N҆AE3gw<S >FmJy,3%8œZ^p D+hֱd2-aX}yIsR])`[ $dd,u1 >v[WYT9ݺE=E-UX8m) '  ZH&Z{ :\6oٍ&<,ŭ_/$ZFH{#b;c/%(W›x.(:yq9qt!v%}T0ٮv RVsBOy/޸CA5XLME-$8œt̙fW/7Jga{ƒ^:,Uѽſ uPfw."Q/CewCT.%   PiYrv$@$@$@ @ݧ7-b~ %g|0Vbk!āPbF< |oXh6)$D!:A IDAT :#m U4vnenWK˴TѶ:F-[eR* vцs&|s\ĺfiw(X'# HRɯM5.ZOEJn>%_7/l{v; KPS0EgZ w:E$@$@M5=ܰڶm7(++i=TSUmuuR甙ؽW G0)]:鉗R8]#2ݻu}I:.~%$$nŚ5 LWOo]Tu[n9D^7BGIHHU Ћ# ?&c>xSfnaa)j1d[%eJOH6cå3gmմȑ#5L!3g3fhCIט&QxPZ߷W YHp Z:}.3 5("B`rFYQ~CG B `Qޑ=:u{^p^݁z]3U#$Z*#蠂T1k#"[ozjƴ1!-k|ֺeuq7 V"l\<*|,_hFc |uTqƳ[xQ>a=e 6F@qzPM61EvΕk뗱{=ݪV?Ϛy3bb LG(xVJ+ǜm;v8a$tÓ}ܺks p%@7%4Ϝw^hxWc W/x#p7lxFwϾ#S'Qlf&:[齤I]ü/U۴iѫgg)"]<{-TdrqqމD1:cH-)33> \Hr˿Ngqo)\= `);uk{=i(#ƿv|j԰Lxe)c.?)Ǐǜ:w9*HXzLI1Q -׮7'qB]wNy#z]sRa|guzF1 uN9{\TVhN;#cqWݺXPvWW\:Y N&<R `ezׇMb~ղE{~alش>RMľJJ/Ld>XA= _pG;g2lsi``x:55؈/Y6 ZQ-30EoV~w͟)B~C)Ud&p]fy!&c!ӊ[21DaM: 8:xm۾JBJawdoK`?t4ꫯȲ=Dǐe+W%JUG<pTWez7/b׷euj2ihԜ$F-J0mT,\g)FMҶ`iC*#6f Q*]25[o؎+i%>Xb遛PX&9aN:*cƯmc*#X k: t)Q̍/7~آ. GF[8a ˍ}#>RN}vF:wpeXgsIHHEkCE7/È2~Poo8b eIq WTyZ zkn.tkޣo%11Tl\LL8 g5nX3AU XzTVט#,x)xqx`5ˢn$Zj݇eˈА 8#R"sY;k`4 >b(1*B=k8za[??\᷅xG}76| ȼ,HbF4VHp9:)'ve___oL+W/$Ž@KaI+ [BTFE{MN$mB <``MBQGJI9 6BݻT{SgIj 1얦1>ۚQ#] j׆6B$@$@KMۏ:o79 8#:pQ$@$@$@$@$@$@$@$@$@.B&ZHvHHHHHHHHH D댣FIHHHHHHHH\M.2 3G: h]d          g$@3u&        p4Ѻ@$@$@$@$@$@$@$@$@$@H&Zg5L$@$@$@$@$@$@$@$@$"hud7HHHHHHHHHM8jԙHHHHHHHHHED"n 8#hqԨ3 E         pF4:Qg         !@ $A$@$@$@$@$@$@$@$@$huQ$@$@$@$@$@$@$@$@$@.B&ZHvHHHHHHHHH D댣FIHHHHHHHH\M.2 3G: h]d          g$@3u&        p4Ѻ@$@$@$@$@$@$@$@$@$@H&Zg5L$@$@$@$@$@$@$@$@$"hud7HHHHHHHHHM8jԙHHHHHHHHHED"n 8#hqԨ3 E         pF4:Qg         !@ $A$@$@$@$@$@$@$@$@$huQ$@$@$@$@$@$@$@$@$@.B&ZHvHHHHHHHHH D댣FIHHHHHHHH\M.2 3G: h]d          g$@3u&        p4Ѻ@$@$@$@$@$@$@$@$@$@H&Zg5L$@$@$@$@$@$@$@$@$"hud7HHHHHHHHHM8jԙHHHHHHHHHED"n 8#hqԨ3 E         pF4:Qg         !@ $A$@$@$@$@$@$@$@$@$huQ$@$@$@$@$@$@$@$@$@.B&ZHvHHHHHHHHH D댣FIHHHHHHHH\M.2 3G: h]d          g$@3u&        p4Ѻ@$@$@$@$@$@$@$@$@$@H&Zg5L$@$@$@$@$@$@$@$@$"hud7HHHHHHHHHM8jԙHHHHHHHHHED"n 8#hqԨ3 E         pF4:Qg         !@ $A$@$@$@$@$@$@$@$@$QA:,I hr1e^:K\ z gB$@$@$@$@$@$@$@$@$@ hU$          t`} ˋVUU*-=xnӈ- 0r{fMssZፅVT8 &IHHHnhb_QT^P`VQ)TYVse1Ffx @ ^ ^h-88h1._g4FHHHHH,4њɺB-CF;oYFqMSTT* {MۉT+CIM#^&J<^EL$#yƬA>! 2 g$O]@ͦNߧw̬cǢ7}杮8w<=(++S۶YRc%]/Ixca*u="   pj4eܽ=<1tZDw7OUvM.>y(&7|HgɭSY'eaբXHHOsԛݯ v|嘘S h|g}׮։rZqqR7WVVn۾zAIHHH\ >rQYf[^<пW=7WS_Q={ebatբXHH쳋zwr^ZnOz= 67'Nci/λewg#65$   UEkn~B܊_vzv?wy7[s,^lֺ&Sߋ}7(HH|[׎B٬_-Zut??.]:̟7ojwIX)L<\o6fM٣ a~'S|6SHɩF!N$@$@$@$@#@/ZË́вY;S۝Q|.dN ggn^$ xz5#j 29BXHH\L>At$řwW/oݺͷ?9@ջpbbBB#6˫].$@$@$@$@$@%@my CҳNJ5PYVܱK"=dbxyןR*,N*R ,O$@$@.C`ܸW"O5'ǎGڳp'Nc @ ` / #l.DXei=6EHْBh>|+ d4_K֣v~Q nּ͓ԤwPPYqQaVV;oRYQXKJk5arӞ}B[Dzxԭri/+*OOO9q4v䣇 %ؼMXѻ_cu߰OiA>}~;Lr9&+!ȑѧN+6?-ڥ*B&sR,@ot´ z[5 {M#tиQC__Դ'c6n܆*L]'v lժE``}7lVZZmK)?IMk{#C2ǍZg* 3HHHH Dk!X(zg&Zy 3W]q(\נGĩ0PJ ݶݸ /3g<~A>'O_H5~M뷾bYqò kq4h[Hyת5[^YvvD awt3乨RkM@qͥNxmpŁhS?w3Ͼ&Nin~G| Q"*YvӳϽQl:]VV6g:Nt07M_۫V7&_zR(<| Xo~h"IHHHj!:e*[`(ohHܨy IDAT!存ʊJVE$ '>0y>>I CG {9C^6Ǝ:n'rnlJAC|}֐U9'(_ "X+//_JS,SKe>Q}}V c}x*5jK 0{M:]b%S>3GUX]ſi2at\u#ܱ,y̢jbn@F3gz^P70it\x! Wٕ6$&rswΟXw~&]Z Qi&L:^/7/@_S||uTw>@rQ/tl/Zdٹ =wz @-!@3 tVW,L$`m'} +Ӥf&O.ǟ[ ~}ǼPsm.n?^7<7EDۼaW! rMza& > 'z Jp|nFDr|k?aʱ_$h1k֬)qq=FNWGS&8QqiEPT6_xܱsLIIoyG"wW~> Z>̫~%$$=v_aXUx[fN3jC!߿^vTQQ~ݏJQ w9waĺEYH:e%ҦM^Pd˾|9r|Ǽ+-$@$@$@$@$4:4lS\GzB֡Ur$`Z#7}٫㈾âvi3z6u?W(ێmZthd+V\s:hq&VnwیRe_7&ZGDc4oE&Ԟ}UwC_lY3r[o<~$GD۬YSTm"+kŴKPlԺD8Wèp1~巁ӳAzz&ڱca~ɗ|sG)Z$@$@$@$@G&ZӨ# ((OPmG*L!hܵytzL2+?a`py7N%ԩ|0c7<}Gԡ$іC` C#־lQC)Y=;\XZ;{}!WWZAadU ?wv1%(:릧g>|O;K-ApwQ=.^ԏ# EL$   mhu/<];;Pj\;] >_ֹ8g;+o 7楦HY- IF`*e+5)$/O7YHbΰGerP?d-|Bw]KNN$`Xsh`i*aFPc'-b˄#VH9q"XuM*|E$@$@$@M7}u-8r}e@יK :vA[cY'{[}Y,9^|nXvdYQQ7$toןXY\@jjԑF]$WeIɓ* < 6Xc̾NjS< +1Y"܁T,0-f:%YHHHH֡Gd5L@7W{Zu*GG@{)q]uﻊ}rduwwocޢ0,|w[R]X& #֑GG_bme{z5 HʊKG3Pa] s$nƎSf<^dkh4ieX]cJAVgCt6D1zW%ˊ s Ն4o[uM 'pbbyy59*4v7_EUF7|]2K۸GND@wp{w8f9CPŶ]w;_eG3F!7Klr$`@+JYkUG*7x7obĜ!byݷ}1Ժv 6BrI%{buULg" @-$"m+ tQs+MkXtJ&^Mjn[)\@ɵwfxک/5a!A(/چAT\ho۬S RY$P E~~*KT/(P)Y;:vl':ڍԢi{j> mvڷomuR{/BCV:wn\ǎQ*"   Uhp ><#ex3UIte06fVd13DD0kGxx>A%W<]]Yq})aCZ4Ld 8& XkFFE=;tЙ +UQ,Y;]s*TZ?X@F\ %cc]1AرSBN#"ev֩mH,cMTmî+1L'   'ecĎ^^4ۭ{o7:H)M*,IԹJIBABgTI`~dڧfJ`1pvM=NC FIčEؤk4jLg}H9zDcX9wԋlZcHW_*)Pۗ̊^kQү~^4hg_C  CA;oFZS/6֩"%))51)ɵLNnoXX}/%%G0َ2c^ӦMK$eؠa] \7I>pZZ?v&U:q"F4potNQo/\p*b. Nb8* xnc0W :]b5NJu0RFvׯf͚ҲeАVZ>wo|դIXcb)gQuO=uYHHHH)ЋTyzTymY\=}<|;כ§n/㲌⌯[AiJQ֯ D]φN~S iBNG ȡu M=%Y`Ew`(/rb]gUQtW? v䧧z!࡝g̮{-/>S}GUtv//zE-yWu hԱs|BBEqשM^ع('GGFܤKzڰڍ5aJYQ_Y@˖e$(5K]Wnەۥsyf K7?9ڎÖsŎa=f᷋lN:4ٲ &`pX%yG{W@eG#<}'c /GohaEe k.> uv:bu$&%^\n]/eb]=P Ng@&;%yw$W\pATTk ?C8 b+64,PS7?xg69(++4촆Λo}f_->>OyXn]0Fҥ-.gϮ}FE~Ww>Ʀ={bڳ v>ᇪb`+fb!   pX t`(.?^֩meFatN읅ǫُ eg/ y7|Jaf8kW[/"W?ЉU}yk/EɇrK$a^0)Sn{yZwOO a,r/.$h_U*ٷ=pU/xq< -@}df|ٷ{ke%kӷ;a܏?svmػ|5__eeWݺB% [\n׬ݤ^$@$@$@$@E^vg,:25¿O}lT4 )W6<#jC&5\qߴe]g"3W^kCa'$DrU;}Knܴgouۂ3 wݴH!ohSQ=yKΧ%4ʄqܖMvl[BkL%zaLV.owi478Y]_?f_Oػ+fr0SCo.槩SӣYD3229amv;ŽoKWܵSy_?`mmJ0GmƮ]y lٲY@`raDٌ̬08n޼Sev Ye;Oؖg]sxA}uϜ9^xgnnub" pkӮjY Y:'jyZ̤N{E6%{;Y$($@$@$@$@&pcFC+Nq|    G&@/ZFmWV=@;U}VO%   F~-XCBmHHHH`Ǟ- &yy)i]bݱѵ+ :P<         +:+^ '         54Ѫa ؕMvK$@$@$@$@$@$@$@$@$@$F&Z5:#         ֮x)HHHHHHHHHDFy$@$@$@$@$@$@$@$@$@$`W4/ h0HHHHHHHHHJӮҝ]vz+b_^ܸD,&JZy{k/c,$@$@$@$@$@$@$@$@$PЋ62H$@$@$@$@$@$@$@$@$huЁZ$@$@$@$@$@$@$@$@$@5{ &/ZTQiV](tt90ODn*= "#G ~챿7o47ʪUXaEEE t.9    "@YUTQqԬ,$#PVYǭm  5BCz4wg/&85_E$@$@$@$h}"!yƬA>!~MY5Ӯm[q^ؖ vw{ٳ[d0l>x"%}/ZlQK 7 o,RVλ'xȘ?O6nrK$@$@$@$@#XckA]|g!tPmDS m"\[ϺV7qqR$+++mSpjlHHHHЋчl@[ ;z~~t7GDHHj5wwUW(//Gm/..ufWcѮ^a#5#P]fs$@$@$@$@NG&Z;qIe&\VRfp]»{{'?jR0$:^&JԬ3sI 8-S]t]r]VVou m=+9%դ˗sL^ 993)gMǎ+OLV4:#eHHHHHD GNnjy{˄}V&c58^X]Xۿ*?mqeeJupPW+ŕIHH~z' yyoѲeyE5aMHHtp%\:S[    GEYmif߷n:ӨzrZz׍`a /HJN=THHv"6m1yB|hH$     #G]#˴˫++ug-E9-, ޲ HZ ^jQcI:Ŭ}xo5o&=z5iTV\Tzx۷TV0j= Vʊ SN]2!vm &,Jqswݯ1:voǧ ]HrԪ IDAT>us&9X de]~׮Kjӓhwӈ.];4n'? 5-ɘa Q#Uc$ОQy1'L0hQ'ݴ~k/ mLx8g^8lyeA)5!" TVVZ7?ήi| ,)'ЩSԂо<1((G6S?w3ϾvĩZ9aZсM_ۘ{8RKOT 0y>W>I"sYmF߅uNK=!~O- ֮?OBIP>l~gիتUo~_ߞt2t)gygFaKrtvŊL$   ŋiwZ2,=Ѩ= Ce~PRZ{ƩwG E;-UC"v S'`Ok3>;9a#κM{E sW |FC~{g~F:L w:{n KW* ٪T 3 pfÚ Wr}:tYesEpDZϭx~[ZPxh??c]({kwji8\9."m 3Z Ypוhm y0kx]p|nFDr굟0/KkH6йܱs?LIIoyGN6}?CzNN:޿Da+vTQQ~ݏJQ w9waEYt*88hq,wpԛ ){I˗ O;U]i#    p@[8U Xڶ iuQL")i4]T5#H 6@Cj38=V]qzM5Rb\۴o#venl":;{Knkw/~nlH\lj>+W୷?Q PP ۫a]eeeha"'|8##^oLٯƆ 8,GR&g ^PZqIHHHj5ъaH)-{#)Ô[o ǫ#hL&Pye p&Ղuk:sg\ &ڼ)Š!4gd[Sפ</?YHV|B]|IeO&Z: F^rDHVHHHHVphEf )ؿOxݺ|C5Z$+/j8HMz&ٹ0 vיK U\F: [, /sݰ6ɲ r~E_%IH|nnW?&ga5?,OjӁ׿la5cՑ*"   UD+ƣΤ<=* NY?Nǧ*2rŎ {"q^U8Z~K|$G@gJ8e:T>TO#6&AY܈B^Q'6?~AAuu_7BI$`{~션3ryV@`` ׮mk"b1&#.{FFF1HHHH 87:1$ө5BqӦw݈koD„"m/MVfCMMpD'd¯з_]عͺ&jV䰑C'8b$@7@jjZBBM=t󏇄T[oV!    Ch6"~:Ry2l8NJ{2۲m_O2%Qi]N׭qi''Ņ^5WVBAZ(q=x +eIIW? vŊ)_x   @a hޮOyg+HUTN+GSLcKbqV(CՖVQѡRj(+LW"#8M..~}Y90FV.gq+ 2P_졻g%>)'-PIr ~'@ `:LJ Sr ؗ_M_xyχL6bXg]k2*@@Jͅh89>]^S~&& ה;v*-CC9FbِuS!\%3^.*R%*, X{JNΪIԠhy0@jqqX+WtvvJMMSMA аdPfƶm[?|dcYO޻v-Z*yS;k^ ki  `X*2=crX=Cƅ^/kY/nٟa5^@Ƶu-B $\nXTsLGGeTG*Ϯ?H#BVGNВgEuP }69e浰DD%ʵXy/S"@i8z4OQ]fF 83EQxj=ĊbϿDDO5mx{/SN=hի<~o׎\RoZ! [E`]HI)ƒ蛩Y}Jb!NR9;wih%x}55mJYٕw5Cr[i`_vdcGJSN$h4 B;_rJW[︕/y! $Lv|K6zըo會sf]ܾ5!2]J;wioZD'ɧ%}'\WȖMƵsU nt`Wέ_|B\k/n'96V91k{doqW$lԠ{=z''Y8s؂d8x>..?"WdWif9lڼ=&&N6Zj̙PQM|졾}IJJ򱟎PZjU+7mlYC##T;ܴi{jԨ:g ,,䇳I>Չiߑ~%#A͚?mK eҸuk?F1+ў  6.`+!Zk![`C Y2KVmwBr[!Z 'dE>#Zk唰|qcĚ/Oɷ()g'kI-.0p@ׇf4h޼Lj;tqSN# ?aqK|<_T噞IR%&L3Ƅ-[6ngڨ֤fM_~]دo|*MM0Jq>s-ܹkoiLhn/ȸI%c=  @Dr42WᬰcI)ī 䌵v7W&E ?M?zE.XaC.^qRVr̍M1ejg)&u[ʖoӼʤ$ȵOg֮gX,.^J|VJZ(˗D, T6)+Y*KBEGx_.`?rz<6h괟8-qmwKٕ6 yO~vy3\nɖU7h_B-  @]%Kf$.w'h%U&`vSV"3"P\f8vԹ+\}| @5$m_M8p^GzlZ>/I &FGE9uiֳ֥'kΝ->[.w|*;͢$8{~ӆ[7|Ҕ KIJwh牚zrrs1C.~B~hO`۶d[%'\pI/+P鑣'z۩GIʿ/#N%O՗cN$;r367^z_}j" Pz *A@)$-GW"$ge>S^r jJ`xTrr[WPHGϗ @(S_Nn]e2v  `".<$7]egM0KG@C7{@@ J.2~X>  @prrp*]r}Y8D@$.ZK2#  uuw&uplX_GڴyG?W"  rі"@@lL߶hDcR!/z86T! @@lK`[-a6  @ @@, '\ ػ_9|xYZ:kE@ сu     @ Hy@@@@ DC     Ph@@@@@-BZ:!    E*@Hy@@@@ DC     Ph@@@@@-BZ:!    E*XΗ5梺I]U PyM%DΏTK9    eA]e.F@@@@QB6zc    .OJq̬<]Y]JMvAeG|˶ G@ʨu6Z5n._fffQ   !93bJZH =+-6@@L 8akР~'O]te`  h qnjw餰74.YX߷+4Oٺ9Mx%$d;Trt…q#/߱XcT!`wpe_{=eq wbYE75yb,$5! b]w۴n[399zT7emem9Ӛ5 6ܔO>?w7Kѹ^޻?=qiо#/S {>PGr#ˡQt5)Yuϕji&S͚:gK# %Q5&n2Nn!Z[_C@\\\FxOG$e[s<$n+asBɓg {dS׸q:o]B6[2%}޲Ebs^Y2,04  `;f;B}&tvU'Rt6u,J@ʤgUgM$V-Z){$ Qm[hQ +৤>/ٲu׊_ޞ=Ma  A]Ebxд\ѦR*&}#w#o&s<ĸ\`sdSãGr!% %H@>nDt?ۼyǵHww&M /IY|>}cc=XVvFifF?BTرSyFZ*_YNddg={!=MMIMz}uZc&$wG@(hI|utSx$)xRjku!ڔkw2o﻽iR[t @ H}{(\|y1WiEPē*;vhLS'OUOJBoc͚5r|Mۊcw1K葦  m y?.NN2r%&HHR1gkMϞ! Pݻ߯|_Z>5,_Lvq8` IDAT*I|k>TNHEÆ~_6ͫT.[[ߧڏRXR9c6||ZvT/4aUpKUzƏ3WMW ԩ='ڵm)WVuι\a~I6mZ֬활r=*Zo[%nճJ7|_џDm}}ףn8p?WعR'X`-#C+ŖX_w&MU暐qر#}i8K|Vo@ 88;wz3:Q졓m=n\XaأgwtN9bptunXS,&et( 坑HV;s_* 3%PJ%-sq\RCrٛ $&YܕSJp˜SzTcX|;(g%(,899I2\9^{pK=Jy@d>pcO9*O2.^X2}_gt:m{>7p~uٸF;=km1r\hԨQ7^Jyʋw>;r  h}ήNvhl=E\h0v c__y1VZ!M^]rY2.,g[Ӓ5mՆ;8}reRAXL5$I j/U_mWK:R"mݺ0d/5֐8qK_Ai$o{o׎U֭-C^pFV%%&hD‹J $wRR-S5Kl޼ds_0٤LN!Ll+]qv5Wyr\IY/H\[ctq7* Ѹ*@@cBzhsN,;gdm(۝=3C4ti5#NS9fX:>M?K;%\-U50ȯCgD"ǓccήoⳲU̚2Jfz4sl`]j߱C^CadQ6%.ճ/-^pvݪή6dl_OȖ7B{.D v }ΒleSf&;ge2={*!Zĺdu/8X>ÏsVp5WW嗇HtUQرW>nC_0g%4`_۶펼% <ٿw``=DR|7e|~g]v/1[. HS5hiCtLl+/yxU3A޲egL/wjφGN|_;*7 L.]:HW_}ڳ1D%bKJ ^݀~ztN{;=䮪oS\˗CCy֘4qҲkXr9.|h8hA&|Q!DD^ j2\>=~Xu" eVzC"Z9Ov;>>2F.9嶻 f@b.],'OG{Gcѹ۷VP>%skKlZb{Z ~NHgvNA5VC)Ʊkc\l67sGRԳqPVasոd he͛ʸ^VL렠ui̧iF=_ç$e$Zoo޽KFW~TdJ^xS~ǎZdǣ+99p@Y74 rǼ>_/~=Zc&U{mmw+UUL0Bق=%m|L)%z7 X I^QyƋ/ii#U~4NHLvF[F˷}_:x 1Dki2wU(223_{ݭ҉8H5k7$Đ_{˻o#&ofBc@@,doaVlUrd3+lJ2|Ѧ;douS#O}̋ëS+2Uc6yjşmlPcq|V?S&go=;u 3 . oث|￯৳Ͽ*SCVv.3d;g۶4Xd^ͳJc>"==;<:wno#?`?ύJKC'fi/U,Z5t(s/ _%j2V7F8LSʅ$T/\>ҮuQ%Q@&mڴA?\%VN\ڲ^TD2ܹ뭄JդM˖MSB&m$K̒+%ɬWZfW%jt"rJP߿T/(i@ʎWe?_5PRSd#VV54@@$@֒L1wԹ;ʭUnoN"ZeNV v`m1%/I#9yG?kuX8D~PU=zlQ3&JY5^Isgn;ThWO \;9_}9hf{PBڵ4иD-rI J6UVQJN3ļ`(VJheLB宷>$`*+)Ϝ`>yCə3tWTRVr\ln! (,5rCY[lh @0 Dknb%<v;r΄6ũcX61o& 8{d#$9&>]^&p)/߶o]:ıd ͧK羟gI\DZ*5}gf#`6g[A3&g7{HI_VjU$W%gA*厎qa~lqW9ry\$5YǭaXISr~ŘNhtޚ%İu͙" #`ֶ3R0CHN^ȍ =ݔ]wn=h,=GoSBʰ^}';we1ZXyJ3>"%& JBEx[_LI.N9l|VUlF橤 %΍m> Xͭ:8:IֳZuyi)jϞyq?@Pj< ԉ&IQ {0Ds>}>0|۾}і +A\62iteGf0oI   @H.@zvr匶ND[Q" "PSRng3X_FZym\ܫ_]*ԕI*A|to۾d~yKߣsr(};1,j7]^j߮U[~(%%%>åO>8aZzNJJr%46[+VN@@ h WThs?>Wѡd@RQM~(W2i\Aittq'ʁA5Zro5jϬ]Y+ƵS_/@8,H՟ed&<>`²'}*~:B2!M%V !B,yGIGK6/P)H6F9-OBضz惆(6?h8|'J2I{զu\nh  =D{i{7ð|U|4juҢ%  @>h/ist[nnm`:fE/*K?p$]OP_y 3RmtdiTRUP$&&)3pwט 4&$nո7kdXygݪs^lZRվ]_,Tm›qjd_mޛaɐkޠxK 4z?zppePB~VvuGG5aÈ  P L(%%uoڗli{"msQ@J Ju.ұ ҹLOrZb}?Υ$ԌrlA 22ꬂn ,:U˧xվ}n\6b#""C,u02q/7BeYsDO>iy|9Bz~ nH|P<@ Em0f'DGR-?MC] s d%7VZZj*O':Jfohy}UKK{Ds qQXoի>SJڌBlD`}8EƖ"O_WC qDǎ2,6^}ýi`)ٽry͚۵mڕzzXL ׯG6S Ӎ~ڻ83YVjic1wDv}+ VrEՉO?Z 9Yw xuk0Xpf>b@(nB}|CVuƵuZTBJ@u9YEls9q{yTb.R3r< #4xQy|N]/>RuZX懩 AW(^ ={URms*&z)q؉T}f)W5oPK<=u?"7'9QTj|Z!5U1I֤+9~Y43#rHK?-Z6kN}VRjժfLQ拉áyXrIe...죺F~1qgTk0{:c{v4ºuGx3p   Pd*tu(Vrɪ~55]=ݦnbgW.J#OjNw̬#z+8:4w:Ziu")Eu"PaKW*=w~#O0D`"O4zaLJ_;taOI͜spά۷&D;K. peD0?䊿Bl 5jpW&pncUm}/UUV;AU xdX_ %KV˰+/C{dě+;~:GUmXXߖ>3\[cт9o#U˷}uzj`_نt͔lгZyM)' JZ5YtJ$=M=Է#IIR>=H堎ZjU+Vn_R./7m^nTɫk_e,_=H~=YQ !Ba,N Y2KVowBr[!Z 'dE>#tߊaV OMx%D+|%xyӼܤRj'.ڌ`uh- 9<; }˼ُvK$]Ӿ#,n+txmy_(%;nIU̥{HSp~T\ B;RRUhyWfX֍=)O 4Fy* )Oiذn&ۗO@;|`>DK$7o%jo7n| S-˗D(///O%>+iaVt\itW_{O;|֮dz ڗP XG]q(.NJi;Aƕ&h7%j4 R& 1čcG{]PϾaGIALnj‘8-[{umbtTԙSvl=a]zֻ2hࣽr?g.{ܬTY%6\ܺY5i>¼|XჍXUk9*-+#3zս.Ypgʅ FOio{uoӦE-KJNN~=co0- )Z<2N5kip6۷gNK-W.QZK?!cڵ|z}kc羼,|5%l)mHNRю_,mZH\yEIV'Oݼe$A.0-^mOT!~~nn .\ڲuע *Q.;DM{o'yM3gο=lԛo_+..^yf  @ kТH(ѝ/kP+-,I ѕH$ge! f9պJNs QB_"    Po;ۚ@?㦲uйy 7[`    V"@@@@@:h(     hUP(B@s IDAT@@@#.Z83      "@V"@@@@@:h(     !Z@@@@@3     *hUP(B@@@@#@:Ό    UA@@@@!Z83      "@V"@@@@@:h(     !Z@@@@@3     *hUP(B@@@@#@:Ό    UA@@@@!Z83      "@V"@@@@@:h(     !Z@@@@@3     *hUP(B@@@@#@:Ό    UA@@@@!Z83      "@V"@@@@@:h(     !Z@@@@@3     *hUP(B@@@@#@:Ό    UA@@@@!Z83      "@V"@@@@@:h(     !Z@@@@@3     *hUP(B@@@@#@:Ό    UA@@@@!Z83      "@V"@@@@@:h(     !Z@@@@@3     *hUP(B@@@@#@:Ό    UA@@@@!Z83      "@V"@@@@@:h(     !Z@@@@@3     *hUP(B@@@@#@:Ό    UA@@@@!Z83      "@V"@@@@@:h(     !Z@@@@@3     *hUP(B@@@@#@:Ό    UA@@@@!Z83      "@V"@@@@@:h(     !Z@@@@@3     *hUP(B@@@@#@:Ό    UA@@@@!Z83      "@V"@@@@@:h(     !Z@@@@@3     **e6Sa/jONi%jA^i}  ~_}\ W/ }a'[}~<{a %6a5W,ȵ{^5x_}\ W/x_}a.V{_7z߅b/!A&    eWwߖ̤d% Ra C%Ѓo7^x=} s/_}  ~_(&ΎȞmTԍϵ5>    )Nn']It` w9     @ D[FotJk^B@(h 1#]u:),jz Kmjh0J~E@@E[tӳ}Y鴓[tM/ .W7gKJjΝp5aYYYliedC@(q좵[A`=I)An.E;D֭֝ @򜳳!;j4 ,>%%xn]bݞ=>E?8#    .B@,. YN%= /-6z\4Q5ۃsfVy |gtJOIN?v$d-2sIQJ=klSvYFIONJvЩ7Wa>]nWU nQkZb,! 7^ڱ5W@tqYo@$ͧ$оuk4Z*nn ׏;~?rW埓_}1pK`*{=.ػϿ)_4lXiӼJiiiac׵g[{׷0FFF?p8x\p&NHHMMkٺvW" nB6};hwgݛ$#,YnU.>[u:6 >~?iK}%iMzgÇ;a>NO0RV+e74Kԕ%æ#N慴  dee-_v)11wdZ{4hԨQ7.gz{=|?e}%h+aker8sS}GB^{$RɘwРF5j3wt!  ePD{ܔ'Arcf* Ǿ>g_declgUoBu]YxO'S kwk&Ys ;}L\Vc vL&֩S{ߴk2/6x{oquu?nݺk׎'17]/i )D@(좵EC{rvFn.R('+egfޟ]7Rfq*[nLê6n&DFGJȵJ`u,}p=sGvv^xg֬Q2ӤlMm6pj5V z섘ʣVqǔ5%傒ARά[sb8W/A_YvE5&$8\RRR7XI>?i(%*T˖]b]H常ޞM طufR+'|Ql&${zfP>}z q/8899Zk.͚AVwv+ Wy|pWCӼ{*T$/wH ŗb7nĜ;"[e'3@@@@Ht`/CVIDLT*Zsr26#Pl;tyaj)}vzݺݫʿTW&ǎm=rԊ?Uo,?IYYJo;aFG ܸd yq|x*AFSR E6K 4}ֽb%m|5CUNjVUXD󏳻GieJ'C얕Vx!\m||%WRrIKKjP )B6z;ys<) gw'$=w+mSu'ǰ4lt%L pЇӒ4fx콕dl9=nuĉcnO}?ӌ% w;[L.<+QZKZ˖fr)T! )BEx_v"9E{ :{)B+{*Y;z/U )DD@JWyҹ3{.ߗke7kc})nG^s)o(YGkXn}//' c"@ =lSg̳ <=uzvv וUnekj\H  @ D[ZZŔ| /"”b32O%)hunh!%OIJNnnm3ӔXgmNS=7 zwwX "q| ݭ[s'   ] +.+5@vrL`87ht0ji1 %\ =%%-v6ȩY浇;q}p~;wPWB&m5>~Emz,I~OtRnF3L;=9).yZ?  2"@x{'N%])3OJJr%+VN@@ KmaIf?!Z7{t\o:NV $ R$pz-dA {~&OGo޾~TE+OֳƏ8qڕЯu3lMMH˖ff[o?ZCY)Y9˯/ZhrI *7hP*@@2%`EL-6S6#+k_BIIHRd㭥f#"p=BRC0J'O˶rɱ1YQK>Ϝ11?J qEw [PYFVY*^j%K  @`;C"ڣI);>y7ww LO7쒗BW6JXIz`Uռk4>1g*ԑ$ڻ{T˥нBECUjֹdXAXtXY<4i >m ;XM 6.NrNiCi<8HVA֬ضm p/?ٛl,#{׮ETV%b#}cР~JsB^}ݫx-@@l_]ue2N՘qmGFK(} ׯUi,k3}QY_ź*U{~;/ʳ2Pb;uQTB sNٺy-,-Q=Qrm>V:h #OWKF}Wr`p?~j- PZ=,e}T٨Q/&>qjmQ^t{O2(5SM/^8^~ԩSA/Z0zj*۵!>+ԭ?běyf  %B]u$ꐓ`f6,-jjZM[h=ݦ^]yGJ}5;{rfVڑJX)YY'R4P@! /9^%ӫwʗѥ Jjj7s9.nߚ.%47]?L-?aL?>/o+ ٲ)>2ָvΞS^in%cZ{q?ɱʉa_WM#  "IdSm{4+=9k4@D&9Wqq!W?&Sr%L4ka11qaԪUcΜElm(cHRRt<=uu TZоi`RMPFY?=aa&?Mz(9NLP6|&( j֬o\w?eX/ƭ[1YE@qBu Y2KV>BBuOɊ}F@uK {jy׮Mx%DKi`Ѽyv㦜"#G@>?~”ޗx3===11IJ]Mg [lݔϴQII͚U_Tj44aj3 }#Zs׾9R<~3~~_"qKL{@@%@ۺ]Ž%gh#6:pk}m- P0?M?z]!Æ]@㤬>cd՞qSL뚷-+z55yII>Vk ά]τYh]<쟕Z/  P*/_;YhelRVⳲ3Tj,k]]~-[wxli??qZ+Wm럎2++)򐯵gv&-VoоZ@@%.Z_.NJDʤMnziCd*Y7uuA=JW&P% GkHϚ-['%:)訨3.zvúdweG{>~κ[OReYtgopqfZRVaB~t%xg[yb%e8';ZR1'QKʊ'  P(_Nn]+\I'  &]am7]egM0KG@C7{@@ X>  U|<\ʨ+V   `{좵{Œ?{UpdBBz! ^wYbAAR R\]ײ6zAҤwi$7     Hڍ@@@@@R|@@@@@ =#    h     vpX"S&2EiLYh!_>|< |/^~* |/^~*".WMw'[Ш]L~F"(J8`}σ |/^{a*駂/^{a*駂/0ݝ|kAQ3     `,Sr>9..x|YP9Μj٫jD9mF"(>8`Mσ|/^{a*駂/^{a*駂/www77if/>>єQ#-.+T     l:pg     !R     &@v"    Hjh3     8)Zgq֋     E`*     lhmY/    hH6     ug     !R     &@v"    Hjh3     8)Zgq֋     E`*     lhmY/    hH6     ug     !R     &@v"    Hjh3     8)Zgq֋     E`*     lhmY/    hH6     ug     !R     &@v"    Hjh3     8)Zgq֋     E`*     lhmY/    hH6     iyӹV gq>=zWנxٲ~"NװM.'%f'gZ~ILv5Ԗ|iTңbNj/DukШUޛiu'OؓiutC<7lnn8E{HjPvNmԯS"w||­;GؼxHŴ@@@@ln׽F"kҬdVR/RU]]] o v n͙߱筡l`)B^Hׯ8w恄6+U,7k֤*@_Bש]p~~'xS&μzLf@@@@rW@7 teXϞaFYWS ePJB<窚.-G06]\ޣ:zzj7~_BϦf~~9׭i|(?k4%՗ʕ+mߩ2:    @ h7״yIcR}ܫV+|]+Lm*X鐘xo\+V D +W;'1f{5̩_0E`:KVH޹hт[}H{'Nm#F)lֵ1RE+ _;.RP.=ֽN?)ŦQFAnވ3'[˴sÆu}4_MM[/|wz:R1o陱M_0{}n jԨ"Bn֮H@@@@F .+O뒩R9zmeMfWUVGg˖;yBQ@9kf'덲Y)a%ܵ{\ƀ!%_v󒟕l׮m e@w}hzB6**zNv!Z SE@@@]@)ZʁM9|(\Yjz=tU~eێ)lr~l`i\o߱Z]}iJz\ qo̸_y34˕5^Z:~_] &՜j|hG@@@[@4=t #*)Z9N+U$?+Y4+drsG]kf'Gl\$Xqby4Dæ[eőךqFAسw2g[Y7*o@@@@O@)Z9ڨX)Z9H+hX:s;Aݡ5\8QG˴]Zl`6l5ej\hݴyOk6\q˦owxKcw'UmÎ{啕8IIYO@@@@bZuzb99A}yb%}y ivRW {AŃ%g1I5f-$Dl<, lE3    C$mt*TsU6zƗ-7o-C(5!!r$=y\ N(_RyeģNЌ    `wl3͇ SOʠUJEچKlvL>xpGK.\P֨U۟o RAn7{QԀ*dRW~JSYQ?)ӛ]AeQnn!ݽ{',ٲ    `(V>WQDH)D.[ϟRYJ޽K+\$#K+em;w-#[6]ezҔgQEE&k?6JzZ%9!/ՒcJ=(a.m вgA/<+l    @ h.EۤY.Wi *˖CJQkπ:wSOܨ}Ҫd6WE^rulO>k=k&fW,0vŊ?HKS    @. hUwLDllR8?)fKzYQ3^MR >aU -; [ZL/.ws`8,Ыg׾OPݺm? 0    wyr?JZV~L_:yws>hˠ]ʴle;t*-/))-AǏޱ>ɜ],?k]Zh׶Ŵc\Zs@@@@gmhKUKUPϟRrM3M*w%%JZy#6`P )P+7 4沥2=BCXлZ)g~I|\\ YKhE{Nmޙ;EyYTT̙$    @ h+E۴ʁ,-ӍS$j|-QK>8zȝyt+N}yYf!ݮ]IM+\ ٲJe@ըU$Gew8TBɁf}]wpD;fkFظ!\I@@@@-X_*\8͐çV6(uZ Yb![vӓJV_V뵑;rn\#-r IDATbfk_{lݒ:nW/{"wl3    # hqa' e#&r}zcs9Tn:۪MR!g+|}}>^:O^֯'V0"    !k'Uy) :rv5i4=^4J)S^-^ܫce`'Z; q)^cxbK#bgja5ǽ+*J^xnr!>>r6F$2    C!mfSR}ۖ WR jV/oWWԭZE!>:,9y5HvJBT.wPr6™ׂ߰mԹ)@@@@  ;}aHЁpE֬]TzjJ[)eۦ])Wrc\?OO+<2xhQr,лwUK?Z=~L9F@@@@uWN$YCoMӶl\*Qk;i`*W)4vRC==\Sthyk䣕ȝ)]7/F!c1qnnŤdI.h푉    0fvY$[Փ5̭aもR&˗W|X.eܥMR߭}oKa$77R-[uQ@}bzBa{h*ߴ҅l{ZZ0ACj57ӰmUύ#cw䧠g5k7;\Qú<vs^Z!^ އ    #Z -5Bnk)*Ged#&%)gKz4:z0M-n>j/{weg\Pg=xu"ug5j`+ԭ[sú/:4{[L{Kz2wL̇4vYI@@@@J&J*OGevh\7*n n1,Y`y؈Ǚ9j{|X$%~Ye yf5aP@@@@@\)Z.amZrϯ8zR[-֩ku''FE&y>\ G}2 ()+{ɲnY[iZ@@@@@.+׳$     Z)t@@@@\X>    S=@@@@\X>    S=@@@@\X>    S=@@@@\X>    S=@@@@\X>    S=@@@@\X>    S=@@@@\X>    S=@@@@\X>    S=@@@@\X>    S=@@@@\X>    S͞s~˕|ܸ,[R׫aڵ_ލ?sFQNt5psI ܰϻ}^zQԯݩS,p+Α#'6oE442I    J溾εYm+U,7k֤*}&@ egL_vu Kp|NM80{͐q@@@@Y 4ҹdl$ex9Avuu}qpZgSSS޴IoZa5B_}\Άz@@@@ S_r?S{[z)6FƖ(sq<^57&ˆ>:׍Uɻ"{7jbZi--[zk}tt<K.^4]'DGچ קxr3{y!---(    (MJ~JRSpt_0{}n jԨ"zݺuXv6¬@@@@UaGmr*h|e ?8Z;* .gǿ pU-- ]J<y7~Fꃓ]:uV    MѪU&$qx.Uu{cMʛ(kus9^_t:z'K zMJ(W9C,Ў    -t`q~`\g%KFGIOO_ny##Q9Qkv]g~3Q_ 8f5EV9Nl-QlT 4-{OԽ{'>_e/?FD@@@pr% k[2eL\kzTڕBrLBR>I֔.[nb< Yp (/^o<    XpR:?rA!Zef^~n(0vŊQZ>X"-M_%@@@@MSv*!)A!Ztm9% hqԫg׾OPnϻ#   E@iQoNL.r~-|=}:}Avěݛ-U˔ȨWsH}ht$ҥ,[\ieQd9EHkd-    Ch[ЦX@\m)oCyZv.}52 뾿pf@2M:799'Ȋ    # Ϝ:ƒgHHOK %oW׺^{w[]5?'$?;@@@@pS~:ךQfjs/+mNC!kxJmRRiڇbL@@@@y'E+V~=8'kv#oܿW9Z_ŷRMgxZV}u!jvi,즫[#It%??_ܨۂR(k7,=޿Qúj~zͅ ?zjՊ'vjX\o*    9pZ 5C *L{kƒds_JZQ n{ KyU=49+vzg5j`+֭aݗF=-&&]%W G  |wKcwٟA@@@@ W I *jj7&>tk     F-Q@)\ 6*Gk'@@@@@pX^E'2     9Eke\B@@@@4+@V[@@@@@H:B@@@@Ь)Zn C@@@@ E{ @@@@@@h55L @@@@_1+D@@@@ 01@@@@p|RǬ@@@@4+@V[@@@@@H:B@@@@Ь)Zn C@@@@ E{ @@@@@@h55L @@@@_1+D@@@@ 01@@@@p|RǬ@@@@4+@V[@@@@@H:B@@@@Ь)Zn C@@@@ E{ @@@@@@h55L @@@@_1+D@@@@ 01@@@@p|RǬ@@@@4+@V[@@@@@H:B@@@@Ь)Zn C@@@@ E{ @@@@@@h55L @@@@_1+D@@@@ 01@@@@p|RǬ@@@@4+@V[@@@@@H:B@@@@Ь)Zn C@@@@ E{ @@@@@@h55L @@@@_1+D@@@@ 01@@@@p|RǬ@@@@4+@V[@@@@@H:B@@@@Ь)Zn C@@@@ E{ @@@@@@h55L @@@@_1+D@@@@ 01@@@@p|RǬ@@@@4+@V[@@@@@H:B@@@@Ь)Zn C@@@@ E{ @@@@@@h55L @@@@_1+D@@@@ 01@@@@p|RǬ@@@@4+@V[@@@@@H:B@@@@Ь)Zn C@@@@ E{ @@@@@@h55L @@@@_1+D@@@@ 01@@@@p|RǬ@@@@4+@V[@@@@@H:B@@@@Ь)Zn C@@@@ E{ @@@@@@nC:֭/Y<'[{g'3'M|EYxv܉˜7F '99~ڙٙ\ XlXE7Cw/lg@@@@Zm^  V~||||%/^7aƙ3Z ԙ9cBRA} oӺ `/>#љ7?r  T'(V-k}6%,]vvig@_'{L 笛F~cE.@@@@]$rjjꄉo'&UNiCWg}a>JKz50?í2+OVZ IDATgz/_?Ԡ~mc>YQ~ְg5N2n*BB>[0?k8-L~󵨨,NCVF9)ڄgs ȍ    @ p6Oys,2uYNk7rқ UT~ȋs.,pa^>P~ׯzժWLF*w|y,pQcG֭[Siuuw<= ֬Qu}[j&W%o^dQ*U*Θ>^Jm߰MV'o%)^9,o{z _j9С*-FO2vhb>J؈{Vg۷4iR_=IdօQ\y1\    Y EE|7?n\^Fzo29)ICTlrr 3忖&'9V9:;wUHSN7_>()=AJJdo0Z z݅MC:HWi,U;?qν~f劅1a:*ˡ/#^ժOs'h8ӷB~2н{WBI1U|0pӦm=@@@@I(t]b:[yU^SfEPz>|_Rc.\y&a~V,jߙAm.55MlgWF0F@@@@l5g `IxoM\=rĺu[}Ry[^ti%5(f;DEE+rWә4kۢkYn9d*E|:P)@@@@ E çfM%%%ONzz߯}!e/xzgsuX>UTT/ݹ{ϰ[xmm a)իa%YHj,upoʫ}U,UZ\̭jkWs6yB@@@pR?٬ierK~zR'$$nnnsL𰾀> %޽gT]eJ4jTli,YLRy0&4}>[ 3]M߿^pwSd*N8D  l޼ATXF66_{[7ݷ #f#Ј    Hh*ePef'NY,%˼0+ S}ժU3>R`BB2B&?ca)))J/j:J``իIQ^oMw U:߻}n'tҥi ʎ׽}Ԗ;׶Sd*%H0W9ϙ5)9YodVX(ImnKnwG]t@@@@p`]"%xyeKu{2c ,QK9>9|ٻŋ 'x9j'6mZ5=vR*tkѢI3g_tu~ GGŤܿ/4kHJJXsjjꛓHFp8:ujH;"#mJ~')RHkϝ7>{܅޽R@NV(_Ft >{7|`D݈=QֶI}.P@ mqjRBUH^QzSxrU¦/^S6sL)S&D9ˎHY6;~O4n\߰C2!Ve/ (QP_XPb%;Iq#8ۧ@@@@@.+׳x hݺsu޳_eQS~ӏ}flgRU.߾ӫ 㹤E Gn'9{էXyhآ+tIX*-yFΝ{gj˿ffIfWF3R%e" ;ؾRzDžf+ z|aFymbf#䠱ذ%Fef[Scz:8@@@@EB$H~Qt̟+?]ǫKŋfyyy،QYiirl~V:1ա|拏J2uټeNjC_7*tkM퓧4?+w-YJ&cU rf{x{{f/mܱsJ޾lO@@@@DBцzWK%KR E;rdV͊˨+E]]!>jɧ_:}nrSܼ)Sޑv+}ڽo rT`dWxg_ɳˎ8c%\ ?^OKO,)Aݻ>.ߜug%t打g%QpU+c*~7߮ p3}\VXS2nNRkX&DGHܹ,z)[Ԗz7,$nF~|r1=bv>[    `EBVpKjڴc27    N @'d    Z E՝a^     h`Y"    hUVwy!    uMf     URZ    N Rr='X&KD@@@@()Z- sB@@@@' E$2@@@@@@h+ @@@@Dl4D@@@@- 0'@@@@pRN,@@@@(@Vœ@@@@@IH:FL@@@@Т)Z- sB@@@@' E$2@@@@@@h+ @@@@Dl&ٮms=N?Օ&I    Q@WHI;#P(ϗ)Rť`Aڵݸuy`     6bϏ9l蘰7ǟ6]pl7Gm,nJ;bǼ1bd-)vz{v i]05.-V?7FM̙    hog$jԨ2pkm„]GpA,]g\W,>T^ s׹W.t#o\\.@@@)Z;WXnQNMJJ>]Tptt[8b}A..V.+=bRn\B@@@]Rh{OKKU\]uEn٢3O*O7g$ƍ[|9\B |\=Qi:F>/?:ݒυ; D@@@ @6w멩he˗Cq2#\:}EB*tH @ G~ViJM}=},'&CX@@@@SNњG_v߽{O9HۨQ]KC-SgϮ .tnıcwYQmt]:m۶EU"#Ϝskn4Bо]J:2V;oUppIy{KzD.A/<6|잽MxJ('tرM e}}bN:Ol)={D^Yܩȗ)e i1uJ~y'4/'OHT&V38-?    #@6͌rϋE4 Ŋ1,9 GK7o6ʣ.^2nŒ3g4WmiؠJ 2X00x_}eW|IFs?2s3o[a!_$+ze+oRz^}+WXhvpPYݭZ6׮^}mJVgxfͪMS4 =z:AMvWLr˩$+^KNQRtVq @@@@ w(tو us3NKV=Y˗5_Ґ򳆝eǏ8ɕ9b˺[՞]:rW ʔQ:l޲X l"do|-** /uhhOPmYq.))V 1|ʽ\B@@@ȱqr0ǁ1jb{ǏYnM֭˖],XFmժ\ݣG{^JK%ȶmʹW[6zv-P뱳.|:Fa{&C53r@jՊrkK~jGiQ *fN뫯qoꯥw|}4?zˊ_XQim K0v    8)Zl}x2g 'Qtp'WZ0Z^=DҧPAϼceHI*ޜ-eg_ܱs߮Yb|yw߯3+s>n4MXznV%E[li8=ky3l_%P+^}GB*\ &dW}O[ZQNQ_ɳMOJEW:uVXFk(^IUaa~Vy.UJ}֭}FS[99'Pd*+6n٨'NUkߜq9+?h>b܁Gnܸu9=zVz=(w1*6@@@@)ZK2.EH+_]jTPUؽgG _Vy'OF\?lPK(\ҰC&շ|J6lܮ}rwzv8xҿkh S6lvɚ5)1] f'/,mvtܷIf#zbJ ɿ?xXD@@@@@S Zۭǒߤ73J?/Y]W-| l+:}R1c{Wv}Vhܸ"E!jԨb СP9lU-RHy+3dLJ_TX,y7Mez    c ~JIqgf㭴'X\\|z["k?j붝f}ffQ*9U@R ԫ?qaZUxy{)s2XkWܨK} =]_-QZ@@@@ ͮX.?zܹl,G5A>C^f5<"t֯}Yv}bbbw]y Xm}cjm>핢 l_4aQ \5u!Ŕ)&yd@@@@g EܽGTeNqqy83:PRdڵ=zR]GbR\=g5gan%RS\!@@@pJRyW^W >r}ݻu7oTf'N[[j]J+Wmd9uoll_:ulh ,ؤIei5\WaѽL *IMx!bRrEX@@@@+hOWWIJaÞ9j~ZywvCRRmUVھ] (Knڤ.E6m6SPmV|>?q?Ka'    %`kܚ39wžiݼjՊfWHbTblosi6F}ö*wUPNU˦/qжB`6s\I5?{59׎Ǔ}79#   ׍ ٱccv߿??QQ]^^J[KAڵmѵK{yլYհs4b8|]֭)|ݰa">N_hB`i _y^ƣBRK|+IyU%#VAX@@@@Hɓg!;oUBYo+ժW6WÔnrZJnB-1Ty-s4h6nYuKaݼe{JU-2JU]4|no!!?k<T9i0    )H|ZVC7Jc!5HYݽaҧwwK:vh=wa7`iМ[?*[N __M*qdkb IDATEZi*Nmi9uˡAڬiƍ)6lП5UAAGw ̚Z˗Yb$%.Q#V|']_%9fDn'    EJ 6Ӥq}eTV2jx{{mENU_QIrO?sۓ)5sqVϞлcnnsӲʗ n:]B6 9D`$xK3z5nTZ5}(Qa9迒 {۫~ZRASc%=jdxF ʸǻwC6--x":;gJ2!K~ڴI#˖=Ci)[&d꯬b㥦>3C+A'L~bRJn_ i)&]g    f\*VH4{ ȟH:I64Ƕ-* cݞvӰVW,{J?f%CHKNm35ӿ_v瘦m]xىYi\Jm\$ȶm+䒍2_򸰟lڽ{Β/߰)5nVnRs_/)q /TZv?    @(t-霔WbI u~Fq߿?tR`Թ_ׯ_azҒ(O24?+reAT8v6Zr}2 ^y3=^2۸c^#ퟷ2ۓF@@@@'psujm_A/oͬW^ػ$WkXA9[ _T  E 7E6o}l*.((#st`TpJ˗^tџuB 8C %_Q_^[~vh?9w+Ga1ي<&\t4Fo_@mѦ6>֭Eu쫼WZ9{Gzv}HDH9M9cMOS@‹B]%Hu[tb=(,mسpɭݐ:|6E-+ѝ'~GIe^toMŜtmHʯ;aYud s.[*WNQpkݿ^CBel2>svs^:8"8_Lj"@<-\r5k{_ ڲanpFs̵iʼ`4vXiiEieMH{lY.:8QO)ތX큕|*gW5>Ѷz m>V\W~Y3VO `Cxf(v͟Nhs;6̹%M玍-c47K%7n &fAj|6c11+'?mRYK>[!sj|VK6%>+gpĨhoKTHhsֱCZ5)R0KWF:u695YºuаAe|u7 b`l_QJrѩc*+V844$}tMq)dkۨ[DmH:uUZ4oXBa [[p=O'2Td +WZhenjWe8o9{汍<;b{/,^Q-7E,`|+Oa??_NyuէW}ޠVeTIʭ"u4nlP!\mb KF#.ۧ]HX냉fKۯd0ncG鹩vĴ-[7*$W(XFhsgo(Zwj/kn2g煹J+*eotzzbb6Mم $wyvx/']`̒'4؆uKalExj( @2HK.sPu^b^u ohܨ#H~5C|kڦu{+w}rJS-ho]v-eކeÇ W:"?%K.{c6oszQIlbg~.4nzӹsWv kzwZU$$$mHH&al׶k#vJQ3dϞ^կ7nV9֯jޖȻ\S۷xߗmCΈ2drybȃ}[=w`ߒQNnM{d_@[CHp|x.\^g_Z3i–_'J'(]:Ķt,#^:~t -(U'~D ~Ε68ħGcK jn{Ey`"WByu~ӎePK6|Wۜ![B5kˏVJTRf64_ۯ?qϺ5F5CY)>k5EsH%OE rE_r' ~viza귟>P.,zʇ6[:gIȘ=җzP_mg nc!9} Rڌ->=<4Cqk5|Fuk;M.K"ꅜJ$v 4x"sz YJ4nl;k1ݯ+7^SbuWȂj^v΁dΗǓYm# ֨Ud-Ocfhv5v[r0} /xho4n@vP={ܔ~dˋ̡!իWh{->]2|EJSoݸ{שˉ[~T*t}XI7Cfc)D)˽ٵ`](,]\ٔYQ?>zT2h_(!tSJ[?tP~ ͛P:ڦN7,:gpɧ,%QZmƯW-fȐTD],%GZu=֮r\Ƚ뾞|p TN>([[鈴A$?}!nIBlSWY*+!#~fk'|(JxTVe^j[\/1wv/{ؑsGPJ(uVy˗bn-OVĘ뎉R_>uYutPHhTzIuNM0n!}4m|7?7`@d|H/vΛ#t.9*MlEEP{:[1yA֓se<~q^u{38j5hYբVY=}5Ԗ${{tyVZĨ*B:~O9t(.²uk䯿>*{_}vdlٲ*)r3G _ns_nT=GPlJu룵yb*KTn7z"ְC dRy zԎm;fMŘJp-@n1[B-ݮrRx#馲S{tbfQS3ݹ)RjluE`.16*A}ݷ\I }ۣ̝ L6e(PA{#H̗{ϐ>r v'=hZi5&Zoùs~m6>mM@Rٲ$V}c9.#Oʙ3zqOL|VZUmذe} ,N&.-6ɏn A¯6oV y۠']ZJزE~Y6:g" +WRd,.)9ʨΝ3\>*tk ry0Q'$Z 5~c$3vnVkF(nig;}yrzx+:kY-Y)~A{Avl8y<^(wd6q(f[Y-뭆=3͔9-' eWp Qyz, 4(;)ީ~w*&&rjAs`旅e~lzv4~]Q6@<ЭP欄e/Z9*$u$\]\"?K0u-qJ]yرj+g$XUƙ2ŭwP6޲n T6fU2ˆ=LB2H@܀k׮9isڳ"2rA 6a0(,^ To]|֢Ŕ+ƽy?,7+)maղFMݚ9TF9$JI7[@Ԇ/+v{B-} ͛UOL^_ڋYjOD =* r8>O^٭^fr r.mU$@W/͓r+"[)W/&<zV,Am­`"A% c|T pgsŽvcn&%s y7He^vkYmg}z \Nx/gcwLNBlHVhC3 A%WC3g{Ny.0$SnϚ5d#sl޼CnX0C hڎNzEFeFn+aR%:9sݗJ=Ǖ+^#K(Lwbܾysͤ b"%ˏ(h#wvob!+9}-z#N{|ɸGӰ af>MfM _tvߞ/>rn_ RW+%frY> `^Њ,u__ek0qRE|Y0p$^$gBDHV!D;/ʘ1+}z1ԩS˗[~j֬ۼeĉ,Y H J7kC%M%)OޡhۋM'BlYЬi5D\JzKh]ve~n3Ht~ΕYqyo^&Kb7c탬BUTKhCMdW5}3`f8Ms^mtq$bU&F&%fX׵Ĝs慹S @@ }+qCSa|TĉS}-Zp (W6jՊ=zt޼uǬY4 7U``DY{KoQ#Nv:v!q:FʓƓ?rsFY8p }%?)a'*?2xRn%}?ˣ/\m"uTףdŎpаRwcЁHR石;J/d 9 LVpXB3L[j8^hnmœDfiҧWεsn<(Dkot숽Kt#Rr-nǩ-j]4tē(!us$b*x\)VkhM\Q磭Mte~_ rh\vT&t61 jܴ`x`sf5 9/ Wz L~"~N&P8  EGt/%j򥋗}طnsB>\^xI#7G}wre2e hK]l-"ԸGPL)tv"Ӧ?ĿNQ*_wCs,5u4C`ߕ(N j 5Y MxUVe[%WϞ=jEDrV*Kh-[lly~Q]t-:mXSIcP[:K4n&?9Kf~۬t^F-e o6x;A{oͥK,CV:\Rs+]s2z=zib_9A3:^X܊myP,,eM6"Bkֺ?*ȋT[ʿ]>T3~ ՗tQo 캝剞eV(9gSF]YXۆ w^3gy;l[tVٮ R;k7*d_9f-[\ԪUUetKNyZ6ƵYOrGKp=(LZ9g֌E}>f ՌsP@5ؠmq/)ƽ8߿jt2kX]c{v.mS~ ͛/e%ԥgP8IMJdX J5ɞS ފKzlգ]Gcj (f;Vz oNS%zB5Zy:+R7&[Wk+&I|6g=+uєؙe67ڛ087S}=yvW{2tm)kqmv}ZS~ɥJUcKǏ]:~_=5QZ{tZ/̍gz/7Mqү?!=HV!ڹs]~C~skuC%_B+y2ɓoJ+/#h]/RP֬Y+H.L"o,+XTZϔ/_n۱xfy)VS&M%Iܽpޭו@[ BT T#g<6Xk_)g.SIm> ?uz̐5_<6\ۡ8xxз^{mecdVK0W3gwݻwXNL2~6k ֨n֭'}ر=!m4mZ?w7oޒ!,OԀOfbR{-]:IiUUgzK.2 >1t˯&Liܨdf V͘)cJe] AxTv RJmf-{/DG__K,֭[#ɺf弣F۹k ZR_жM4iHoʕYcǼQpO&|m4 zd3g}ic7rK.gl\.iU~Uk3Kɍle;>mw_ZfWϞaeh{eʞCzюrm|X鶝K=RIY/>tbbdg*?Di=kXnvJԦNėBB T!12̥_>ܸZ̞]ņR*VQEeigɚtSARr5?>dUIV {쌖le Hؒ_ .>d수{_izdb,d;ԠaʖAK6k)pֵ+!AP[gп|\K$!S\Rt!($$8s06#=k5 +ʠ1*Xx|ӆKGT|g,Y](3"C,TxJT(ɒQ$VhYv%iu5 oeʑ@.PR+?V\@ʦnRuE5$vUk41/N r7n步7@b 4z, bRbWܾ/<w4cL3ˮmz4my>7mq8嚔?gZ/oYcbgH{Ԗyv2:T吼bnAcd1o9;d)ռ۷]8/!w@Z4|뮯j [Brݓ~[s]\c6s;*3H1v̈́-嬿>hcbfmDLBwL$vҶbrd}/>PX9e@^T%J4[ږ.F|С-[wGG){pzwDE ~ݦ)dV=tDT g)KzSBG+F:]V-do~ЪA9j?|Sa$?g/=#8KGxsyˠ 1ˍoʏlCOW}9tϔG#t&z!Y Z0Ϩi'g?/=%ͷosH$ʛlOA.o}`\U17/do/ʗRRvΝuh?;*i'7ҹQ&qGӳ&1lVF-sk[IOFWȽ?zڧjbg0rp;K}=;IPo ɷ.W{bX٬>ϟܾͧ\BLis6eg:)w,54Qxbʋ/A^Dlp 8z0~y!Oқr'm'xq5JSM\cfYGW{sӺffYƯw^.^7kcϙ}[dߟy\7>+^CV~e0`{9Y T LN*g~ A (>۠@!G/MW6]f\&md@nϗG_}_!S/3妀&~ͷ?/X1'zjV̐K/Osg^pUlf.}:@&76\ɧS} .7QmD%>C iҦ|u֠QVh-bMŻс䑝q=EcwxvV1={m d(%*<{gsuk eIfMy>,bpU+v-+; Wv@IPnA;S} )\޿H~ՔL?Mtb-;šUJTQY .Ե?nDݫZin^ȷMxl)_ߴ~7+.Oғnt裩5ywI~fI/,niޮiAn)nqvL3KuƟ$TgEv1^ [_o^ VRjX&WͫWS} 䭑>gJ y+VU SvrAl{9+u:R_'oݸ!>z/tjt]*._pI}mF{с=} |-?iؠuDO@~8&K#R'<ڐ持 mh&yh6kf4ޜ3"F @@ /֬QE闲4!4OMq=V,^Q4b UyS;3H4-hs`A&1)y; IK%wُ_}9 +On0%*a#ʿȹLa`w+*]p [E@@@@ E с}r'׮6mk1!     G@@@@@ A@@@@.@ֻ G@@@@@  00#    z     `Bz@@@@@!Z6A@@@@,@6T    x D݆#     @@@@@@hp@@@@!S=     ]w      D``G@@@@ n@@@@@ h L     wBm8    Xm@@@@.@ֻ G@@@@@  00#    z     `Bz@@@@@!Z6A@@@@,@6T    x D݆#     @@@@@@iJG^Dh+mM*R_M!Qa  p}ZE[~O>נ?iS*-<8KގN NvTI^q>ܽӠ} u<}`E";ud3y J-V9*lʷXJ]3gOƨ-XVlٕ_#Ί=Ǽ|۾g-oo >\y+ߔIk1 @"WBTwv_Q-&yKLt;u Io \3{̹X_skC~cs- $hS#@سgߑ>,[*ui_7Æ+_εl"v$Aa:ٳ(qBNhC"] ^QgthgL+lM Sh&sœLg΂  8hmm% (k׮|= wZccƾQխ!ǏP^Nٰdze \v]%q.]4WRNhSi)u/ca t޵`9mjOJMDfF3I D@ 84Dwxܹ 1?mڴ->d^"' #Uc'Fo6g4DiҹnN {;uxN!D3L2Ѥ,rގIe^ьwVF3iE@ y 84D+ك$$jqV5$d9pUD{[%tkg@Av˵e\{-p9 F3!@ 0# E9@+H(5Nہ=^Nh^HK\QIuh$4F311oH# ܈<뱗_zJ){Fnҭgȁ۵C'OnԤnC[hK]и&1UP;thYj󆅅FG_;}MZ_-GRsѩc*+V844$}tr'OmذekY<)"u)'O}vj _O9~3\z")-Lyah|x5nTw ^yk^~Gڵz>G^⑇;WZ1W7n8zIJe+~:% oZB+}3KM.޸!I|t]٠Ciҧ/ݶc%>SȵOEnS.YtrLEs}}0l)b5 fmhZ7=7Վvevs_^ + ɕC= V7zܙ6,;vshDB@*D|Bypkzw:u!!iCB2I]{ڈ;v{Wǎ^'88-C RClȲ}Kto]v[%Joڤу=ʟ/it%v b2I/0J(*?=4l{fͷ-#~K6s{NmvnބomQ^s 6U?#?يo܁}KF ;}s{ ɜڐ`\vjϾf҄-$a)Jx♊?. AI/$UCgl.9cmISjDiC$&ѥR-Zᯟٵ"ZJ eHAj݋/3f;o߸)[oPq$b+?e:tY~WI.s%}/4O>e+TZnߌU__-̜떹e>x3UT8*LwG͚6x F\J2T8޺K_oҤ9[@PڟX l?a[|_ssYqݴ[|V{t2J7{8y|3[k%iwY  P/mV]'m|Sg_彿nk(Ϻe.ѸY:Vk=ѯ+n\BkjZ*=]s`=o$m|VȂ5jYfoe4m6Y~]VfL_`9K76۫nYs z XEˇ~{J@m,8xH4iJ/ڹs:+4lP["Ym+GՖѴxE9ٗ_rc;>l9rxpp[Jhuӧ3sͪH5P+Jg~VK=zjǶbL%V`N]7jK)W/R#"ydݿ#TV}JbVMPm'GRzI nn/fʑSr]--ОQ_Wn cR5\:~t߲ŵ{-ץ$JX|edߴ-C* {H9FegK)ϟp܂Éww]>=!GӖk2y,56ڛroZ.! w_jfZ~}^U${6Dyle(  8GsA-iѼQGogΜoxE娲V7C6>6Ͼ_Vٲ:8SG$dvEɶ_|U{#|O֕}d:5Om}Y՟} ;%4WVJ93-nK~ afyOg-Yɸ $^ecM;f ;͐dT+)3e*\͚&y]:v6> eF (SFM BN5OZ&-BM"{yآa-&Go߹#uE@g]Քi%H?DkcGUUnұoY51…F_$hCRcfhv56ڛr0}u {2Yp:r+e(_)G17'y[u-fz@) h̊}&1!ɬ+Wz{H>G Ν33umVlUߺM[qnbJVBETѐ3(ftN:~U+ŋID 5~c\45_|EF'e_ʵr_zx~AGvl8yį%r}EʐOŭVvϢyL3e3yN6D.6%][4ekO?/Ga_l\욪<{կ$&&rJVVڝ+[ ({|YY ^rHwIzO)Jix캠y\lJI7gb"gc|II3+@ۯѴ /<[YPL̮*K!d)TD}aܔ[B* IMDž%K*ȩvgp]➷#G6mcN6ڔʠ*{2gv'&oz2dl! a؍Du-Ob.[^Y( X1Go>>lM !B/ El*yU`/HUlwL&3I}ws739{\zEi)-,;6٣0CEsm:EHتtcwru},/OrTo XbNB*EL57o"PߢM'z@C2:rLܤt4MX[nRstq PJbrnա33` J<ܙVlpn)oM ~iU\ G_ؚAhQQ&DI)2E$G_(տ8xEѪspϦpTۄ_Q {x6%;׏;KϦ{S ɗ51sf`SQ}%Ma` @ 4Ds\lfvCFFuz:5cF8vPznIIɪ.xӳG ŤSn޼}'NFZS^.k1'fjlUH Y]Q*`ET7a6R=J{J LE)AOBQf2\J1К3-+%*4Tn ?RD* >M4X?FKTÚc(+)9:J@mɤՐ%-F2oŦ^nnQ{+ʊ}m; %뉺I|6>YݒϦ >M߈tՏ'{STo@;* &%%ߺWw1Ros3(?>8!3SB%_y: Es ?%)`m2Ew=ﭡ<4Nz3 fZQ+{kg0ic=^zK6;ptql.BF r/3O}Gl0$iW ]H/M5YATçnMhm3& @JC8=*(**/*<$[~/in^4k5Kmn ⊒QNhGu0i=6*єs_mYVh*S@R]g'ܭ14ݏ:Z?VJa [)gn,fu1m=lTn=|#r'`*S,?zϸ%(ydgY2Φz>etAW %\?h[RRʚ6KFF5 mۦ%-X.c]k/Hm!) %~|)L0前TrJѽS{haV-d0j}<&5v ͫbƇ}d tj{+ew)tێ~49_X#(٬w,wO{Yj®!@Oh( MUi L<,A^t^L[zj㙷'!ں(YW%Sy~5U+][; ICdl~,~Zqf3|FAxĦd;,/7ҝTԏA?8 h -RǮSGۻ$fƎS4D+ኒJ']3WϦ)'YXswK8޴AQtE ZZrnf SXXTd%$8xږk+V?rO-iN;T\Q~I>0}ӡ1hLSho9(Z?M`) F#[DQ}+Ukn5]MӞ摱M{1UYZ92h?g7k~1 CV(-j_G;晴BKfjN׭~kl\dIxܢ[ٔ^{Bj7͚ @ ԫmFFANFF M*7˓SoպOfļ<=Wt}QﯨZ۩wxm1_ݿ7Qr{gz66SNewrY kT|]^{s&3o)ji97[$PS¦1h)&qN2)|"oҮ(G!& e5Uӏs6ӸzfJkzpoO*۾av=RaE/5M㑋,gwQMy?%ߛ‡npP-)Dd5!-eܺu %U9T {l}i! MU1U ~L%~~>5MmS=//))a+k(n~=zOu(u,%=qUVơLW:N<.**&'Z|MyBI8 S;e$Ux_H8_Q /S 4UlySNNl+1W]5NWQտذ#MqrS(|w9AbfdS/oҫ/;6=ߍZy4 % h"0;3ԗp.Ob@ycId<*WX8)3\MlB(&uuͩ׏ݤ3U%ۛ+J`062L9KruR\bIɈn]US^6 o"9|H{hAw_Cl@3ʍPJ&D5Y?,nigSO{ije7yoXi>Kb0m(+ X)Pf^eN6>,,@GѼ6 R){SwMݻǦG0Uxlڋix7 t //Wװ zv5nN%.6l)>>)/O(D+(xwjB5Hp6el|%`wXl>`SND5͖V`>[AR.郡͈Ѽ{P7any4(,̈=z)l5dDӾ[D4 L9=dzaگ?Ξޠ@ADz1w0D¹9KY_h C?xuk%ReoZ^ͩJ9 󶼕%ӏ+w )z(&턆}+!La v~zN>hqdl5`sGHنأEWt`g4 ;$Mf<qguwK>~K7wP-׽isC|G VMF̛ @W@gA6ydAc}nZl EEL@Y;xPCtpKw7kofqBvizxwڙbnnA  ^eeeƃ{֢Դ'qpHSx=hc/(RSӹDyyyuWj1dpҲ,¤͛5yKޚN.~{+Wk̵IHH~KT<`φ%%4ˋX+lw7)`%EJ*ӽe(ښoLU3UIΦWS&PNZFӥcVfyvnCno_1ooܴԾvҡC[JVCf(Ni󒏨 =Dѐ;ӿ)ǾRxǐjHf+uu.7Ay9MmئmI(Ed}d^thyg2yҩ /k=BG1D'77@Q4m -Z2kҳOHaq lպG8:dl )3A讎THZ<hҫ_{q]h_e>dp vϦxu% IDATw\w)˧,VtMҟ3&.77_5%rݛ{hCb[ BsT X*`ռfjٳ5K<6?);~[{?/yXo>3{Zu14#:/դֿOYZ1l5Ij\n\rNGȩZƸS"(Lu%\`O|+![S j/Gٔlr?ķ~>2 U̸>l̚<JN_\\w.x;C4~m>&OAuEE~6.Cy}wt <(Ŕ^B-y$>I + mkݪʭӤw?:¨}.YHAdi-}Ú;OOOϸyT\¹k)f_osrj&O2IrM H8 Ϛ+Jp86v}vN1*OlphĆu|b\NEóSD 8s@Ҁe+o5: ;j6x_Lɕn}3[N>{6G.ᚬw,w5gO{YM=!=@гW4VJd7tӳ>$n+ҫKA}@,*_~s5S@fhORYOBMueBL6g15:4vƓ/diɌx7>K|+׳ Lg1NmYIΦW-/@B'sK汣SKh* -je>+M(&mLZ 2pW~/2H*ʭYRX@aCK4R594Po 8H8=gX{{Z7>K8ƿ?Z&L\&+Y)(LYZy¿w >B>i=|(u|sA9j|jAV ZIo33riG/&3T d<~K7wPm)|ZJk>wov [r޻HG(Abb gKNK7|(%q^zՅ:3yϓgΘ|Dhi E~;ptZg-g/ e$wOz2Q%&&?~zg_.XKsv/((HPʈ$r[fJ7mָDQcQ}ARm)eѕ@Xķ~> F˶Dͼ=њ[fi0^^NYY)i+=Oh3 ݾۏcW(.LaAVB!Z]Co/H"w㈨fPzޝ%EP8Źsg @hBӅɎMw_d ')zob .՝ǟ:I+l\'-fSу)X԰\P;/ex1UQv˭}A` jB+r]~>=#|_'?u k<2bCˏ+`m`G/I獃tɅ.T3{~ _?$`e2%#!K_NmǪ(r;(QV-佪ٚ #2nۓ J<{䦏^AYIo7AM(˯ʣwwgO{M+AMͭLt\Vޛ<E4-7ʡ-_ hkEh @L@E,8jC@' Vd[G ^?5G=*'+J9[i{s-z @@FBW ^L|jҴZϪa fP XyEjfMzIʫm`Zow % lJSaC+Ϧ C @ D[_, JZ<\syZP+*$`L I%s8rfV: @L-;}8\@uLVj fiW]bPlأr}87Wn8٢g@d@.ZI! @ @ E+V @ @ . @ @XhJ @ @ B @ @+X)ԃ @ @ BC@ @ @b+z @ @d@VvRt@ @ @@BbP @ @N!@ @ @VA @ @]!ZI! @ @ ъB=@ @ @ D+;): @ @ V!ZR[um!8m{:j}}zw۽k۹qt缹㊨s@ @ _F|4_7 w5hlos|qł"[Q[9;k۷otUu @ @' kuvs9CcWVT\r*pofα"|D˻ E{h6|.;{yy={GLѽ3[?5`(v1sgfOyG'%/q#{iثψ:QY?BC@ @u]@u!(ga\^;/SsgG<ιDQлk7Dwv1B]8I-oPNǀݩG &F\|bHp3#GRFj{v(9tC11s-t@'Vn~ jJ4 @ @@UJaaY/}&5g*g84vthEcⶦfߙ3F6J0zTH7yʳۿӳ_9q⌥ؾ>E6?ٴrs6ضû;Whx֏A =(n!@ @-͢ ur(DBWi J˼4]]&{:V |V)M(U򧑣 owf)%$eШJkJAwdQ|wg+47Bm0B– IK+s}kMl?|ĤWWvZMz|=taʷmN z~ jAvwŗ?~G4u8J-ClB`<@ @PT@E!W\4%Tnt~!El77 n%ʇ0njEu:9)`_[vRzYz0%"6oju9uS>ʼ-,,<|ؑ#&Oz i=4e cPCVo92w//ϕwҁ*PpekRRLUF9 @ @%сFӍގeʳJKV"}j~rFv~H"PXV-VuEFxT~Cs_~ye[Q?<{"S2xЃJjЃ[M>t5_|MQ @ X*Yw9k3Vf:آsL.iѰsh4(0p8]Δ0.<|~Yys4<.<cxz34 AL5A踺]5kVS xwp6䟼2r[,R8^wj譱/(+O-)_rˌfICGѾ4uv;U^ieeť M9W`\NXҪ}ʷձOilFS'LZf}QƏ(X``@qqq|BMbݶdV}Iԩc;3/F_턢{v5jHѪh5Z?5{`S&=<ܝ*既¤Bǽ'WT-iÊCnngEx Ёx7\Mx7 v14GaaQjZٳ&jv6= icՐP[ @ I^_'>>恮V5LM8}܎{gΘiL!o5|NJ @ ZB45hU|;b&DP=Um'1hDyi;;)Խ|;laXyf qrP}Y/JNPePnByr{{h<4A/J6}Pp~)F:2s@niu@PJw5~H FkvQhYT$?rwE3^5JM Z?5@svv68Y..+H^_{}YQQOYRY|W+q5'eO~e9C~˯,t隩la \\d> lEh%J_ص9i A9 @ _PK@vĈ;u=1=Xl@(Vn,ᙯI˅xbjM?s4U(o]ٸCDi1rwll䍊ni0ŭC|3a{- LN1_:fm߉h;K^̰zɊRZi/ Md Ԕ1:}/Z2I }siz.7jqr7nܚ䋴& awrm4m[?1s?WST2[xo88I{9::>|cLJjsR@ @. HGs\q|!o"ywwKW7&)qec}|?sHUR`q.n*?.oज़Ɠ1v`w4 򵷳vfk5sv=|4S9?;}4庥zyy6`v[,Dcľ2ވ#cq-[D́SuKn{⾟~cFQ/}ClO˖t(3èCjtO,/9>~^='P @O:qlrVڅbs^\l[XMo)t;)L  ѪsR@ԇ @@ 4z꽜xz3JKIEuY߉OL.]Z;8DÑe$vfrl$tԽTޞJD~v Y i, vA6ʓbCˎ=w3&fvqnI~?L -&<$[)OalC:Eߧ̓ݺg-M|q,8<ʄ~"@ilCXXRZ?HKx7FEVfv-o5I-Gvfdd?qfϞ_B[x꣱'ޙVH1Gzr>WP^R$33l~auU<бb'un-exl2tR8 6%%%Е&֏=дA@LgΜOK*;wPEN15) i@߽t2QZ! ի6^hKdϊVss;˯^|ݭwEj8j-;;)dTFU}N~t @P@r{+Tp)e橱ӵyTȕ\SE;f Cg3wlB%=xϓ^MЕb/-Fʦshܾ>2ݑc}%%%VoXॊ~{bb͛ 3:ꉓgO׊&&&itkxb]+OZU}F3^bvd0a\ gS cҕ ;ql>.u @  :_#C( "3~̢vJ^5lek kڶM.]:(=بs.YoOgtL[A̎viΦ` @꽀JCuu<\p[Waˑ\,F]:1ݖr_YU 88SիhU#@ ]*?^ y8:/*#Bfr+%UOpͧ4ܔTRv*;4s64bFμ<42}䞿pIdҪZ'Q_8/3US4ĉ3 ]DPf?t{d?B1"(]}ET}E}qKl|g>~t@ R@-hi/ dJ[Y:ms(c^'vU %b;UnljS򫅺VOz;9ԧT w(a6Ұ@*[#GMC>kLC iiM6z=ӧ2Gmӕq2<W0'šD(A}J+m)D͸6]ͬpիgW6>KM679rKz6t>xBQÆ `6Q76 ɻS˯w0Wx)+3o~w{SxkRț-ة=o}?(.[9-৛Wox9.xfs~6%cC'{k174ӧ @h4nȟCYJ @P,Z&9-@vb[{VQ٘|,)eV ȟ"fZg hR-gxiT,LĥA'T灻)થ>?q%)5nQ5̫@) 5 $%3^Wb;m(9bKY覔sSKJ]ۺj'{wufvqp6$8- Ԭ)WFw4[q0;/ޞJzMrͰ<1࠾Nϙࣩ̗KoӳgU4TFw:O \+LKG;MCy'SEh&zA [Uf RRLN6M؛~ChG}M9Үm{}w3HMMCܹxfz='>W,_{~;{RRXgӽ}֓'=Su_ePm۾>l@THL['deex{}]ƍ gzشins)c/N^Wu5 8r)FKHH&>>Фo޸qÇ%&UܻǍts_^f*UM G!9 ƍ/xEm?N FkۊkG[D6%gT @ꥀ]Tj84ґL$G ;º^"bɲa>׈畕SZvə"ю(jFYJ,>ucJ&o+oE8C=Pš̬N v'/Ši+LS{E8FSc0()՟6 S2WOc? R$}۴F&ݢFoQ 4fSϞ]׮Yji-Ӫ(+saͻæzqάɓ vLU\N ld%{%MhjOϚԓu(׫V֏A =<9s짧 ) y  PY@"7mݲw)hrfΘ(0=fdV<4+s9sM5߲xgp0͖<NhwQoKhC!]Q'q#AL|ͤY1KQ;D|֚m)=z<`0sZJɼY6)݄4c;_P4)&^8>KhnaY¸7 кS@siX}3g_g~/1bz{SG}3g%Kکrz,}O:|L<26Yux7QO<$##7VyϦ} JD_~΅' 娝Y%&3JO@ @Nh|,āiGTO979Ko(&=߳*)=_3!@ @ @V*A @ @PB!Z%T' @ @ QъbB%@ @ @J D* @ @ J!ZQL@ @ @@ hPE @ @D D+ @ @ (! @ @(hE1 @ @ %UB}B @ @%(&T @ @ BJO@ @ @ńJ @ @@V U @ @ @@BP  @ @*>!@ @ @V*A @ @PB!Z%T' @ @ QъbB%@ @ @J D* @ @ J!ZQL@ @ @@ hPE @ @D D+ @ @ (! @ @(hE1 @ @ %UB}B @ @%(&T @ @ BJO@ @ @ńJ @ @@V U @ @ @@BP  @ @*>!@ @ @V*A @ @PB!Z%T' @ @ QъbB%@ @ @J D* @ @ J!ZQL@ @ @@ hPE @ @D D+ @ @ (! @ @(hE1 @ @ %UB}B @ @%(&T @ @ BJO@ @ @ńJ @ @@V U @ @ @@BP  @ @*>!@ @ StIENDB`alcotest-1.7.0/.meta/ok.png000066400000000000000000003142521437617477400155130ustar00rootroot00000000000000PNG  IHDRysBITO IDATx^xE !PB !tޫ; R숂"" &HcHKzO! ',foo/=y9fޙy7{;WPG$@$@$@$@$@$@$@$ڂ:Se       xLv        ]ê#GIHHHHHHh       *5:rԛHHHHHHHv        ]ê#GIHHHHHHh       *5:rԛHHHHHHHv        ]ê#GIHHHHHHh       *5:rԛHHHHHHHv        ]ê#GIHHHHHHh       *5:rԛHHHHHHHv        ]ê#GIHHHHHHh       *5:rԛHHHHHHHv        ]ê#GIHHHHHHh       *5:rԛHHHHHHHv        ]ê#GIHHHHHHh       *5:rԛHHHHHHHv        ]ê#GIHHHHHHh       *5:rԛHHHHHHHv        ]ê#GIHHHHHHh       *5:rԛHHHHHHHv        ]ê#GIHHHHHHh       *5:rԛHHHHHHHv        ]ê#GIHHHHHHh       *5:rԛHHHHHHHv        ]ê#GIHHHHHHh       *4VUz @ /(״ZIHHHH^A+P  ҥۼqo&&ZvH hҸ0j% "$@$@$@$@$hHNlHܝ@v-=Zlu#>߽su sR\dk Xk85|%w4}0z˵s8U Zl:QQ˵t7'ڽҥ֝ClؠhTaNɦyb _ulY3 X@6RN]X8Y7 .& GuIHHHR[5MM  h׶lݵ6''!G]rʕɩ"   (؇MW\[qw,ʑj x}~<|M^ٹD OM_ʗ/]Pfw%ggǎU6ϩгOJHHHN lXHHH\J-iRw+RUʓvz󃄇.3+' m8My]]R%&%iS>$űS   !PN}2 -qe8 $'tҶhPxҕ{CuE$@$@$@$@ ]CojZY 8DAڙ3':BPPq @Jp}(чseS4F溹͗_i=|t?<&z3ވ{ցƔ] 6𼼽Jb/\vPN#Nj)ˬ?_0KtE3N#+.E*!ugk[+W,iz|w.&vۋ.? dȐ'/ԪU58_iҤ:~o-^y%سːY/^n߱F>|  xR͘&JpC˖jԨ'O̾ }W޸!Ru`)تeʕ cA{\dի^87@f_v^]p?f#Y,%8ʕ3<,XnSޣ2!Q:wj$Ŋ̈́ klZpŃ\46y%{Ӧ5-c ^^^8 1аmӾ]&\&KN'5)P pŲ?5Z1sޘ&yFtc3cK -S\uߩ?'I*E=oЍMYԾi݄;?z୍a@kK®P ̨Tso}xM+_6xg{Q.9p۴i:7eˢT) =` 7 5WM#b *AS'Vb ȵk7b5ẄO7n\WYHٷc`Q0+pw3gݬi}~agZwSC,_c/+ N['L5_IA1`pQVJ1u#"jmё)UĄqϯˆ>rk69^\9֭ {~-kb]D{ OaM IHHErW4a@$ G?WkԀMTJWɦW'UkԀd-2N7Q5sįsjΤZ)}W|QM`qչSiS)oYΝI˖Ig3QFmר&Rs͞aԀdebEagc"Xxg bϓǧO^cRJEހy;vh}).yUO 5`ɦm,PY3#ZifI%KqcG4k@nlݪ0jp(RM4U|<<ׯ"b1[?z-Nvq&A7kD't/X~m|yrcO_\#Gc^ѩvv̈́$`[>ha4/~sL?R~o?M*iX,0#Rlݡ˰2o&eȑ9ƍp:1֩!d:DsY݁&Njw~MgoA)LS1 4" ea/ {IR.^pz5R[}(ׯ =;bcʕaJnA|-6kl)`۷-&?N]^tRUDAENQ`DH_lЏ>WH).[nY"_œ'e ˔+u~b&8 >4Z)bؠJݣhGj&WK2jAi@W;0jBe'rqA`@O!:smۀ 5sJW4jl\CHfkT5* J`臣ڵMehg'`*">T@5EIQfiң{g.Ƽև6F dw}!ּYD5;Iٳg{.|0>4j .q w cQYB'N㿞>&ҳf˪F" acԀ0➌jTeFj58n(dɒvB"Y|->6NL]h.1t1d܄RM4TT!rT`W ?ȒGѽpG*Mn%;MF1o2mK3tG($Xq}|`G@h NmU`~b11񭈻_{qL*OUCE+Mn+KNX]'M9+j*a >3TqhhЁ]*D]\52Mu''&jb X5Ш˯nOD9pf) AYk0' Q\cSCw>h#vhT'{S=E6bw\'M㟸!ECY%<裲ND'Fm;Y,+T(ӹs#R_r PMe&2#ռ{Soesٽa6(%ig֭YBgmI]5z17 1NtSF'"ѭ8֘&vKկWK 0 ^r6\]3)L&O8HHH= ]C vX_"h]K.n4J΢vs%zgI 0>U 5 "Τ?j@XbӊwDKGB}wI׊!Wn٬YT_Ll])NL;y0 5q~* Bdx}M*S44h;/p AtGޖa:7KhR.m*c\p6bm4{k;TmVŋW%widӮyb "Sf\51+!  pC8\B#*t-@'G̵aW'KF Q2څF,{G"0AndC.XlJ+1x:>Y&^^ܽ{ÏFO6 ar٥sQ~Q ,.Rchhe8˔[7 V>&Sxs̞7lk|eы48\kM̚^v gݿSg·;zV:VgWvur&H$M'P2e51|?1]S   7$v|i׹PM:pܐ5UrGeFzIcvާ+: '5dR_:b2ժ&Q;84_pR̖-__t*) y֣+44,JĢ$@$@$@Lt*ZmO4j܋0kFѨa mWώ.>r<Vro,Ϙ>F W | 2oJܐ"onM8wnN7oѡ]fB Vڠd\Φ)f]',5'  Hf证`+u/51Ƥ MRlLQqQsqvݦrL)i@ A=v뫳F Ԑ1cѣȫ‘(oݳ7DO&j9W˘c`G|^D7=i zWѮl;?:0V\bŋ-R@V RA9] *Q"Hlظաm,j糖<1H1Q   "v!e;et<svĤ s\%:u~E:DMRE&vB 4jzqԫsf"ڵYLx;vhY|iQ@ڷk!,M5?)f\5q k   pCnņTg_?&yO#tQX)4BrZHriY \!!u+C '3OoIXEw u~]7!ojg&qf||TmPì,S#WjU*?Ǩ yg<͕H54ѱ /_gקN#6oPpiղvh2ʓA̳~Xc(l&!PHHHZRx$G Oon?l$MJ<4Mִ^PDU塪OLEw&UԾO LߦۉL Huގs̑&*:ࠍO?}_'7o)xMnJZ2jËdwOJ4$ei`sK;vR/Y$!cbVvىj504g(Ro'qI-RHC^ݚ*-[knH4P2y$Usg 4W$;N   pk?O]%DޏMN.yKlV3VZ>'LWWAO4gkM[q1Rw2VYMI/geKF}gWyS49yTy6$W԰Am8V,S#5 Xe_yW#ss+ےhSl)+WۍOH4z{;p4wt5DD B 4۵m.? EB,Ւ:OG '7?WUVhݪ)V 1}</?N$_~w& 1v~}ν?MPyK;vh2bŲ+ZdǢdTTV-H{aAf_V֔8+={z *׬ɓL$22 64'Ժcz?`ɢiZ|ߨQݦMI5YvmnD܄k}_D]1mٺ֭D+SFg̓QMlEW.$iӦ͛7Of *T(#F/]֝vONByUk&04nTW| OlrNxvgف_~>Lٵ#GN۬qc?Qf(e۷(T0Ve_*mR.^2mtUM7$.Ѱ/~qțQgdG09ae+6nV'bjhφ5!k֬ Xybe{nl3֔ ts^d" p}(]aWcu"Oll57{#U:čϜ?գ$+sJogfyQ믄vgԩ c57nRI׉]n#ѯC$aԨ jO(_ޮ+%D kݮ^ÊHY_]wPC,gcسg_ŋպ JMޔt9!v٭KzkSo^pzQ] 2 !O~7qKT}:Ԋ#,M4H,Ϙ'rtmAI5ufƚu?z:RHHHƮ%Bl;sbIi2~x̝oܘp:u\};+}pl>^^Q \ǮrĢ\zyኒT4bL|a11{oE.tTbXD5 f[iݪIJ*/O3_=W>c$41Y߯U4Mw> TZ)Oܙ3gZjþI4nc1,Imqe(Q<1Gp4׀ Q>۱+ڤI=8,`_ vp9% gh.;zʳƊ%    52RԓH"ӈNTq)jIHHHH %Ю& $;"hɎ "zIHk \WXVQ    0L-C1= $н#      y       *kXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 $@$@$@$@$@$@$@V%@UGz Ю9@$@$@$@$@$@$@$`UkXu7 &иqC!܌ߏpg: pK[8s朷w*U*@9D~ժZB/_9]%+      $Tvˣ#߈O׬Q%mZ'O7!zt:M~ZJHH?;jB^'87P6,,NjGFFYAwH$@$@$@$@$@J-Aj_~'OA\m۟|;p~Ңy#͛6,̟ .&#"ts5 ł /\$Un]RLK.k啔7իUÏS?~N_$6j.ʘ1îD.]mڼKc$@$@$@$@$@װވ .&W9 4s'ͷ>¿/[֯'1)W6Lq $    5,2P25+ ߄+WRm ;w!+[_wŌcVeYHHHHH<@w>>Ʈ&=zt ?2cMBתUBώ?I=:ervs&Nɐ>}f լ W:S=     Hna@@EWÆ] dĨh53ZLL_'칢ѢyCoo金2HHHHH<[5<$@)A)ӕ6jX'C5`B$@$@$@$@$_]"!d2wŨ gd ҷmӬVpLF4i\{CKI!lڴ~up_6X1  ,z} :(Eʫtnnط<@+,g̜7iسːeyM-ZPnߎ-[6jӺ):0 ~o#T/?L=ڸu\p)OE oעtpO[7[~0F|2>IF'!}(I¢ <ͮo>`|HXX]: |ƍVTnテrno9}^t ؙ3#hBW O6G9x(>"$ƩSgY#?`̮Z1dg"B,>/۵m_oܸ%׿N0;MKvע샨""ֆ'722z*ԲE#y i({ qa屬Kԡ+W!ȉ޽t^Ib FbعZIOEsIHHHH 4FM?6Ycvҥ:ևǏSBBXY/*#Lv?).& afרI2Y Ըܵ͋@*lp\rJ}Z{/^ǧ(_*B NEY*izjݷn:YiLlkp7lOh!歈${}O ($% #iv/=bh@9~씰k|zjNѨrY8SMN$<{"=q̾g;++D<]W7qP7     g5kf1~1y'qPaˉ).\FNmfwm ]T?+Ǒ!㸪cBOV4E*Oa_$%a~*d_yjH$@$@$@$@$4 Tმup[V X x;o΄G4гpY[i: =JR@8sS"cbьi~+.^9#qNJ)vIHHHHH5`QbjUf[nM|?{6g__j@ k"D-__%T(.9oߍ&e$@$@$@$@$@$ x>~ m܀G"{xTxs`YH)ѯ Wd o ǻ8qc?,     !OuW'D~OO[ru '_a ȏw?ٖIj{b]5|W?MڢE#-Tx h(M4DM$@$@$@$@$@$ <֮(# GڵQ a;vh)}ڨQnuNG+<,X(9ڈā44M @` ׂԅ&UTnテ)jH$@$@$@$@$<ͮa ӻbq.> .zu 05GA0  Qp{qqlNÑg5uR%-Cʕ"F=5v < <-1l9s.Ew-RE I!!G&YfiҸV:Vr(23fș3] /OiWmN& ĉS͟?ݺO}HHHHH)毁3)! <]zdf_(Q"-bmf-I}wN+6]n԰Nͣw ];WNI֭1/1%E_pީ dM);HTT|7P/_U\WXf*S׭bHLl<'!Uz%\ט{+WmP3IHHHH<@2Ivyhoߞvcu>LG?q~˼km4C ŋ5 '00@d%w7_,V.\ʗ//rs=koqF) IDATZ~ /*۷ҹ͆ t` R=vp9Wk鋩r-C@d]DC|ػb&[g߾Cvqɹ~FfenӦ)+{i"!     Okr9ŠQ{LGB!A& }6uԙܹ_2|Лo}B UC*)xPB窥OZ?a_w ~r˗H|(!w v ۠k0v>rn8 QyښмYIfл7{%gU+(k.S&X8t[11qf/TDwլQYʂҊeb'{ۦ,,XܹWo_&Ϙ9豓po@ thߢzJFwN kWSaG{}j?Wao5~VB$@$@$@$@$`if׀w ?L~^V0g'C#!֐[51lA>c]y\j˖\xEXy&6mڼy`[BQ.^Z{`qi߭[7X`R͑0oAL1abTplYR戇*ɉ7.Џ>p96tTX!Ĝ_Vmعc /tSF-Elh0ZǬ6U\HYd]@XI\v)ŅLdApذw1GWDcbHHHHH'oQd&FߒY6G$@$@$@$@$@$L}(Ugώ{J7*C$@$@$@$@$@$yh1MuҶE8@秤*lHHHHHH 5K^"XIbE #pI<խ$OGEH$ Dvgik_}vMcDeK$@$@$@$@$@$@]y)Ȩ1ct[uʁ!     xkx86""޹hJ& Kr" XCQo       58HHHHHHHJv &      ]sHHHHHHHhװQo       58HHHHHHHJv &      ]sHHHHHHHhװQo       58HHHHHHHJ U$@$@]Wԩ?g/WR$@$@$@$@$@ =GԤ$@$`S*X0ȑѣsᗌVr$@$@$@$@$@Ev )SNmӨ~%\z#*”#HQaaΑZ*˷#RT#6N$@$@$@$@$`ͩɉZկ +XfˎD+4kZW|K֦`˔..%~o fzIrܶr{}չr]CE`RwNL'N9yC ".]>:vדhLϿaq.Uxy͈ܽ~n)$h`(G Gǔ Wܔ+M"=22ۉ+eI_Ch%^\$zSam>}:|r^bkn._1u>:ςuTSjj/Cg9sļ\ϻ='ŝm펪n SFO/Y{z)R\ !ˈTD"QlѣȤIqZU*dΒ%s6~{j+2-[42gŧpVMw>'*$2 C&)lUd[zwG.9Ezٲe5kf|plPnݶ扻]莼x>ZH8ᢛ _#,h˟ kn_"W ozFX@9^zcJeYݛ7#/X%zY::@TW_Rn1j@Iѩa$/[UE_fimN/'ȍ2.dWX;nڼB7 ykj4\H`k٢>*`Tڥy[ Qݬ'vq2QgwHxi΀LJPլ"ժܭ[r͆i{'nuʁTC?ZMpM?^8M'KOxإ,V]|]M77]PaBp]~sڭi6/pN&/Zpa{V)TnI^5cχ QjġQSaFϡxy޴IwK쾩VCaf ڴ"Չ~g΄k9)U2Hԉu*|O8U*Ua t-kC[[ؘ\̃8]J9|/\8] sU/Hv,{ҵ{TՐ[ڤN0ݻ(O =y+W~ʖrGM-ZID^xe#׮ވOH˖TbK x4jXkqӎ6y eкeMqj`ԡ9>+ :؀c,@.\1f6cQ?ƫVݸ<ayzb\ ^<@|4/R8({oӒCcԨ\UQˡ+6ZQTjt<24|j_?+_b/\ o{OƵkH #V)>iۖ$BLB7JK8*S|3PwL ̚'Ơi2v#RD-~9ޞIm ^rMV'~Z6/b ?;(Qp!@|s6@`_8HE*s5J0Gʚ%s2+,տVש$:d+B# ϲc~I! JXxK0xpD5Ү͚Ԗj;lcHZNCF,0̚D<'(];0"Rmx+9FnԐd!%E IMIybW+ÉuǥC]0e8ԢNabyszΒQc I5jk]p4ZrҾB@[1/X-Ռi"='s 2֝|S~2\tSryR @%JlRJ+}RUY.NH'uGMKp<֣7v|j (KD|y0mHQRMN& ZḧmUPJՄ}5$e+W杓'j%7aMsE< j`dQGz d9])olF8?XOUfpѸYDԬG$&c#) IH֌Yؚ>C$㘸<`htTs扣&)oU,i O aqpњGN&K鈶 K$ԊؠM='nr04S-L2\qS2nwkh[#Pqxܨ!8ucsƋ=+6-~͓܂ny1jkƃr0߬AՎ˛#Dj5!Yxr0>4yDO1o*ɨ!aEԷ{VP=؀+ŧ)CTrҨ!v!Ct9IwAl̿>Gyy/nuG"ZA5K)W(ZPd* Ls`x 0'0\;sJúZjL6PsK9Ooի[~6z)R>ޣCqDqp{@lUvUawixMj4ea׀ڽ0NnQ0 ,IqLb^ *RU!,"n}v {,]3lWyO/lW$d7luu IDATd87`+€Eb!Dgc t`]~[D4wZb5Qa# Y:xs$[6 :o)Go75~ 3Hnm`̀Gyq_]Lʝ飶 9Z+ j&u]yD CciJy"ȕqyAYO@fƺip߶:ѻWGa)<(j٢C֮抾5`5aᢵQQv[w!&& \Dؘj'{|jNւs5|=׮g-DmʥK׎<9(ޥ>9)M^U<0' ؒxaD[g#p#GN%Y6"@-g,#":bx_}C ]WZ,_[ʂbkUW …X*:!jEhr8 BmmkX'r`C쿖*l= C66llDbwDT.+9lPR:vh&m n(k(]CjCwǬ PR@N5mj|&a/N$cS#Lz:qHI#$vY?(G0=J`GDg,9TynPs dt5 a<ٮC}}/67o:y* />=@=q&.b,9CÊę>eat蝘 v=G:A@8xMжr(㎨TymkDsf X4һxL}'cY]x"9hP5f/reK9 YP$ԝ7Ү/ 9O*hє9ULq-61Y+I%+91G8; GOgǐ;T-][&[wa P8*i`8J:醍;2 J/jN슙n͆oU\@SuJQќd'9.(e9::zPրGoJ.A!=Vx`/("}#|7s!+a/ ^b +Vn**5Sy.Pmaohy͇ ܽ' 7H%mH(IG#GOb)I>*?/K)\@/>U M,RĻ3gµ=Ov T3F*z#|::x*3mBW{-'cL@wpUoLd*{Ҵl92K6+ʲ\-߽Dlpp{~};s4}-zG(saL|]B6S''1v0%=ÖB)fpR|˯ hs,r pD;".P2zA!@%7_ ET @6S ˀՈ_m 807oiߦ,$;&&oX@]^-THe]s^ >\#HJ OT!NDJ*=d~+*ꑜ Uΐg#.Pj^C)ω1Bˑ 4^f喅{NL|+/M6:&N*ADY-Ubwp_Mn|{Z hsb7`a-G `wt1JfB7q9^-q TGɱG :׮W*w:hRCwxNtWaıƔAмž6*#3+כk9"I)0x!Hq""DB^14wOKJ si_(5m^Llm5LT4k'~Zx9{[Q+`JfBץĒbLmۋPv"wg!KcxzR闁nsS Vu[)rVrzhfUE(B^ EHl|/̓ilo.g` YLOZ)+ꁃǷlݭW 2^6PTURQɂ!PY; ~"f*5cgL[ C0En<dLOv5M ~JΉ^ɭbo@SΉ* M-wg0vڵ`M5Um3i7oݥ\]1JrV[UQ+JBץĒMzB_DGr r8::4O] x{1㝓A<axŪF0)nn%E"x)4 UuR(pkpud8p:h9zO56o@*&Bj?9,캡a"V "GP_ o_7n64(GSAPD-ME҇=K ٰqz{ѱBX-f($Ɨ>tX91rTy1DILD+]A|(x aT@>;K-Fc̩(ǣYPm ;|ed/`%00G߀0g^2KYh,45Ŷk6 Ͽ(zzg ~ma(nvƧ$m^E ?ib( $.tj(T5U/^3W o_R<\ r@ww7*u-6&)~ r ڵ $BU#Uf[AʅW^ΝZ2RR%M#}\c9N#=GKip$)P+Õ+02U:\ff8'n~\9'zo*Q _W9x1#X `١} )2含,ņ@{ dB.ǣXPekzavG U>n:UЮm3VjРoUed:<ܒU nnxa_\]ZKHd|:BrJroiղ1nI3J͐@ǺI"i2h@p}%ܸy\jO0Yfk~J")iԕZ^=5#DzmcsbJހj#WLw1IaVkR7Ѷm3Q%C'[`O͞s(lwOzz 97Z{JM,,`Fӿ2P2HEwjsK9h)>k;kW8l7jPm8sCum+0w1p#^tLXzcW{ݺ5>yih#ujW@Ɠ.ܻGrj ">ֽbY:@[аLp1|=Q75jX߂;wY7OKN)VFnE/.ZMuD.h{70Ra%]]]i=(aVmZZzxM>x{A['6.MaR+8Hqr1.' U )Nl쐯b}Fp}~D8H&JA䤑RTuKAyˮZ"s3D̂=B1%?q|XXYMsp,iq;vH筲=!Tn:׸\yjTw.$&67Q`o-oD|wG1VR.[#g];Ba>:'VTk9iQ^և9U/bzUp!AbЃͥzSoI5kyP-ۉFC}GqРЫppEH pa+2y-/f%7n奧E}}"vɁtDQ)FP<(mtUֆM;]wdzU>)~,jeߜyTlڭ0,OUIahr~u\ ]^ߨhyyę< fDլA\>Z5^ձ ̎7l7$(N*!--,po)ʢJ_L$!׶m (eM\kL,ȉLiVsbʌkmo@ωT|C;;B$ۣ,y1ޑ/fӅy|YHN nK 97ZQEvG츊_&~(|=947"[V>H[em%rr- qlmC5ZjCS0#.:ϸIK>vL-@",] l|b5gBrCm,Vt,[uN箠 i# h 9&adYܼm.-Ͽ6(|WOdp"$iDХFLB{ ^g4sbʘ[ހ ?F/w>L9%{Y>UhxTXP,H2XW_jƛGyVn:DGPAp7wn2\9b]y68+prՂ*cENNNp-7ભpa~,=22̝aڸ\*@ "zxgwNtw""n]<68āXzrܿZ6(p;IP6mم-CzƖ 臢.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@쓴$ IDAT$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#@Xs&       u P.OF$@$@$@$@$@$@$`9kX5g"      Ph$@$@$@$@$@$@$@#`oL. b֪][y7Nת0$@$@$@$@$@$@$'5DTʣLpCVU^ۦvw<;-lV%-!      < {g?6        &}aKIH *6kazy3n=GV @#@FRi7OR6m pk]Gi;+IIƬq7ЀTP^y8qO(̭hpkj|V4q0&H$.>CWm헃 f.6oLҮ|Z=u`• @&Pc߾۵mQNG9;;+a8fw%kܹ֬sKA$@$`ak{7?u˗SOU#+w].m(NJJ̨M}I@r!aKB-뗜 u֭*_S.Oy<[l:j?/Y>o,딟R @%/Vm-|Jr}S[SD}Ұ4SFc_ 'p)wnibSB N/0ۯ?RjE)^/lm:R \0޻_K zr!Ye970W7 &s#vޢofL%W 6{ !gi7st7ټX XmZk$SVZҕ{ԫg:ƫ|Кħ,$@$P Pa`SobR0 /3rM- P!Gx|@_߱w~J0۪80 HM9kr @%ĉ%+9sZi5.߮5k6geʔA5nDn9'(vhGg-uiY=+5D9F°qTҸ4ti%:B-ǵ .$@$@$@$P2 4mڰFb6'WjH@;4m&\à}K&+H8HCG- )e *%aYI 6 ,Ɲ˪مHHHhۦy~6Da7ojl4Ⱥjr  P;VvCCѐ|`VSs4.a!z!rG?7;ۓ@&:wҾW`rNNll 5-!>:J}֯MՅw|Z\4zk׎yꄼ[:J2wi6ջѳwZni 7o\?\X( O|6cNj 7 2NuxjUK B][j꒐yرS8~1ӭix暑9z6߾ ۣGתU ޕ`{\jCLl\㘲;yś_{G1tFlZvG|֌T񯝻d'X#&&5 KD;vOh~JRٕ)߸)POjYLFLk,z q튧O a.odaU3s[i.gm;![ޟtϠ=>lRn~ YgLp/rWEM ޠn):{z}mh l_@J-Z7{e䙕Z4WlaٳOʔ-+[gM?҅ kRDx,$P [QuB3TE>,.hUT۾])'z Q/_˒%h&*TЪrqqNJJ/*5xʹ x6kpWo>cv}\FI-qa?t 9) 0GND~b B(;<7SLW!{!2|xZA vg̘/_w?(0S'xWWBfggD͛7󕗟-[w*WQUŒP̵Z+TdH)GP|d{uܰ|2&aWJ?suZ8pAպt &Xjh)5B˔Ϫuana{FԐ]sAk?o\!o9N;2#X% \LK5>-~կ~Cb9~MKVgΚc dC'ԐOIj?D5&°H ]22.5hW~+i{v༹3rPLL?j"Tńx^W~|cΝZ3 V̯yyh)5>9_fC< -- XQ^3_ ?AK5BZqzGu_.RjhC\p;pRC%T_}QϞuG%YX^ ^oX9b): (h1bLiw ܽ|1-) /W{/:9Xp9kiK[6F]8/[~20hpK >s*-93Y @  <9ngoAepV 3efB!\c@}pj̸]_~Ʀˇ<𣁻GkiRKnzr-n9 lwջ:'_}bSZF(SBԄ[7/m^{w(M[ޣ7 |VzL݅.!x;Q#AOrnܸ-Nmoop¦<@?2q¨ϿI0xW˖܊]j s~nAH >piP&蓏>p.wG[J_PQT7ٽSd®#}(7 Fv]간[ZC&uPة?9wOy$Hz+!t_m[ uIoA!uٶ1`]".! ɟ-7 Z]wFx[LDcjD~-ktdB0W#@"_fVmpQ-JӷNy]Rǎ]WWG8o:G }K.-4S/[EPm>bnޓHF+{̴4I ՙU+~=v=(e?k0adQ4S#/Waw:pn~ 2ux~= \]]p–r}睱R],CWpͬOBaīCW  .F1pB|jj'v*xT(1aH=3+w>;D\0Eu> lS6WocNjCji_{B88BP)p3ר$={bܽ{-nV1}Jbp%C l[XϿ<1~X8 (|ΣZѹsΗ,P ~v[Wo|ן^C{tT|p.9R4ڦϘѳo#$@2p?wQvi]h 6G< }2O Wf8'R *+ ѬZ mXQjH͠8p;X@Irlzukg:C@$`@׮w\c۪'1Ԇ6m %]b׮_~5WƠlsoHhfe*95cbs~^C.EZH-$\Ve ǎ@/}Qe_C{RɕZ+ /BI'[OYr o[$vIhIT tR(-J 9FR}wu!@{"U&! VE=A(( ՐRf78 ܏o]q`R]udap *Hw VB -#z];-NkV:YK$8sDPA-NcRK]'/ ur*>> CT +4j%'%Pk>/ = AKPmZvUYc _h,Ԙ+"P%.+.5&5#n)C_A e6iٲ` l ?xBӬY#CDCo{‰FW6VZY@IQjj+׏{UuM&%ٱsS&6rԢD+yKcP; $`4rQb14ˋ15Ƭ(kH@m)^4 jORn;Bx M}`ⷨKpi-\QWk%%w/]PH{ e` "V(7jo: yc*[<~Ta@*(e24ܧF7*?@̎.<#p2$0IL'奉KeOO5 40P![ 3ST}`!=:TאD | :/BTU*H_^8Y5|}5[:yy9KFf <U^ۭd`3+bS R߸cw\SĨ64HzI5Z5ւBq]q#:Y%k }Q(z5,Μ!1ʵjƉ78sEIzWțA",17׫ W|BHъsdoqK\qZDHH"(aQŒEf[SBzEx»lJ|m" J!p%~VjZVu;<5ggDŽ# ^j!N̻[GL9$֭ڡ)CfzٗJoY0}~SJ’pRրzU*CN(>xq6X"gWG+H*  Xj1mt߾.ڳ\3d0$?tt'(͛ѷr65䒼PCWz|377$pC&ayHתW$ OT=wQjvNnH>) A}rq#m-%mIZ̧Wh£vs9ufev*}!Wr;BK),^7}5}Nq~=Ǿ䀣{HT9lN&hڢq G.S>O=Lh |8zTH دow\voۦ{Ɗ6aۭ[9׽о;oSSSAkIIQ;R6 PxăС}}xVȑW\jtQLdune '+/ ė3HGs>!1եN}tǵF~\T#97 ֨^  x8Wҹs;l:#\.bo%~[v#"N ;::ٹQz/6b-ZcwjT Rvmv뮂%Ńf=Q\_KXzy{RBD ψ걫ghn*e- KG}f~7,ͯBG(Kq@Ty1GBBrޝ1&9GUlO6]^S\۷5yaxPΒw@ȶݷ݊ffN}b«i_;OT{ԅs'SuDZ-|^m9 1,vUGJ72Rs3mB  { ǿq:e!|\͞#I,dz tٿߛUVՐdžWYn܌u/4#彽DK!3׬.90V21[}[w:DE9]譵B {%?Drss2d^2IwrEO>"/>~Z՗V$Ry )Ҳ QXIhuP莦z`x2ÂywOW˸k1~&4o/L(ZrnG!U̻0NVOƱ'Wd'?dEsj"Ö&(ØŻ 9uiK?%g ) ,EB͕ß{p!Yʐ['붉vuݸQ nVI]֘<5j/wʻ Ƀr!4S*(\q𑮽4xk‡r'T|1(zb|wZt'g< F+nUߢrXny0;*߭w:e=sҾfĦA n3š$JVy+D/rP23LIq jtضW`r5{ZBbl['_~ҘbCԳZut,mCF"#|̷Xmz^!{+Px'2[80;29I$pҡWe"qI'/nZW>ZmZ{Jx¡w@r؈ȓ/"H@wo~h׮E:bcBCxMzo+ *A@{A+:vh"G`CB;ff?(B@ ) IPDYWĿP?)(t>!!͈^k *&&vܛa{|-7ŊKBBҭǏ޼y$쁃G=>2tԶVpu1EV45؆] ?z5XDRqw||c|&©@ B^hŐ$,/ld><6^!]#F`1MaTW |`촬M6H@p 9ErI$@$@$@$@Af^Uq*I IH,ڵW)G<o%r6  Pw׏ ( @1%@F6 :L"+}Ε;gk `'"H~g{    "\   |qz7 B$^7k|$@$@$@$@ŎnK  0? yAyոwr֒ /w;oqV yiHHHH PaCHH@ܔА=7=9N1IHHH6y-GIHHHHHH$x$gI 9k-$@$@$@$@$@$@$@k( Uk՝$@$@$@$@$@$@$@k Uk՝$@$@$@$@$@$@$@k Uk՝$@$@$@$@$@$@$@k UEUp XЄ! )jܵW){wJZ C$@$@$@$@$@$@y^#OD<xnOD7TkU啾mjdzӲ6lUR      ȓyc       (lTHH Nbѡ|=ya5 ^^}8uf/~7'O+lD.,^uJf=RQSCsGڍͦmnu(mcgz%)ݘ53\ګ#'2su ~Գ EUowkɊ Hg認É~9`v2]{H2܏u`• @1 Р~&W e䨷=T V%thߪKM/_)!![G\f+ 6f{*q^Ln2ߢ?ͷ -{$CƷZcMdbi&!_^ݻOk_|&2x"ϨQ?_3rlSDqWVRjd%gF-lhK$` h^"oY䔐H^1y;[:kRAӾVG"yxó~/0x?N.99߷ ԮUC.nyv V.|2eb%6ヤjoh)5S”gdژ"5weD2 X?gOɅLsO XeF|J G͛7Z|-|lll`A˒ٮ.V;q;Jц)Ndd:Kha}qm74Au7RC3vsn!jaj3o.LFE̘Kl"(B7nfs%toH=qt2e$~}Yǻ׳>$&&4xٺU;¼"7ɖj.iѼ k\ j,=v)ӵ' n$_ ,@c2)BBٰ۔XC+ E\g{K<T>m@êF~ V|cs5aUq` k!s'"  "KoRZA||' v$5^{mGɑ'zkJLlhPg̝;vҾM.B#ro%dkE&2217aƧZ$qiY(Ms~CZ -YH$@f%@?su.!XηGVJfX9 |\BGk#m4JĞֹ[j;ppr*ecdi aW"~mJ\.voSwӺ墤ѐ[vS'UQKޭg}krtsOKLygV@VÖXV-,~¾S'ڡG e!¹n7P5{ ͫZ2e˥O r*#%v^@xϦwTL*d޽g@>.iIݶ5IcZ{  R-+3#5!!:,4'Es6/`[E+5S㮇:I߸$l@$P[FG1){d.+J:tLAݻ 1hXPנ}5m/ аGO!lv>?wţ^pOz Ղxqq񈎱uU7Y{7P-3 fp7} C_y9<{cO*0D&v=G"P1QqwLg"_};thg9c5k7{읜 <{ժU)StLLs7no2-^C]{82bӲ3 Xk3gUr*ض  Iw /V,-נzfˤ B-j:~Վ]R(;  iB/`W7߆FέԢuWFYEsF8j=;LYO [gM?҅ kRDx,$P c>^CMI+|jQ^{I7zmb̘/_w? uvnXNM>)fa %빺J - j][,5yq!egպ00vf|#Wjg]aW 5XԐ7s'OT,.^ږA?WyA?j]Գ}gͱ%JQ5edɟj)5Aڏ=bkwYHȹOƣw 223 sRCk4d:E gʓZJ y3i_Jrz)BurT1yفRjYmQӧMFde8sL+5իc_-+(4k"7(pXkY  15%6&w/_LKJB \}jtSŵbw>~ %).mu\nnqk#. ]F8,5R._s̩$gπf-] {Oھ\H#_}lʰ8XffZ*540.[/g*{wo;h.Q%(w?Zu4B/]4/ғ/mpi`k׭ޭ889C2B0%ܺyig$߻@iغ]aЏ=n3tbauav96jVF_6k%A:z]YB$@f"=3pcYƭl9ulh{{`߯owI';̙ ii~>:ի #6A"޽'N7tu붞={Q^y{(ٵas#΃3;&2,ii郟Ct^CRD݁3 Ȃ<ݱ6D6m ء  8(_h!8(DZ_إK055-߯OaÈi %z]}hLvE%_TGI4\W^yKkH\#N;oR;)I}%_[;#By[PmHCmEt X l{'~E> .ugEA4:F,"OO! {;x)r1*jjQuʻ#2$0&WgVNAǗkÆFDL&}\qݹ~+}3O( &2dw 0CI7׬_LWh[ gAGz|1hQ?8:Klڼ?̟ڸqfM"1ֺ!V!8u&/_\/XՙB`l-uo!)5~H63Qkw Lkx睱B3'V﫠f'"4WZ!*ꑟ[rD#Ew'}(z5|CvB{s25Ŭ~(EsCmJ=+6"kA$ #qge6oUjpizijw͍#DRCL}%tO%ĥBNHJ i"\K_ب D9QNj]r}d0_4>h'2}uJA`yx- w;ͪuON ڍC [hIUlv$ ˱?BdN٫$H% O/>l\!ɉo[$E+ ?+5Dc>{c])*ՙrb2D43v3&M68kJ */+^C1h: |8]RjHZFX-܎݀Ef,,iIVEt(( JAvjH 3RSwLXՙ跮8.:@cVC{K>̅uk}\ Іo ή^ahZNPゕ#kxr_~" {:*gWB"r<Ϭ4ƱPyUC hIHHWnRع)D*^ YT6gG4 Ugb 2$B?|ɻ6## S""4w]6s/~wCpVeXojGt彐0}!u^'Ơmѐ!z9E}KŬ(kH@m)^4 jORn;Bx M}V9u.+ 2j͂$ )9`,,AD fRS'`|!oY%H \?|PRyF);A>5A=U9lnbvUvA׀c!YN$@E@ttnǎ2T٧O@a5jW]@"X!!">|i d;˄RZ2=NG4ZEIw#Pϝ c&ahb_NFb*H@~tQg; A@c)pT+OAL8s5~tZ?HB n| +Z.!Z5#_Ŕչ2}H=͝@YT尕qG e1ғExZU,kIHlmrCj@tgjhv]D2\Pj@![_\ģF0륾GGk"!‰YƩLNd^~ɧ4PUؾ|-ۣBTД tE)2[IA-AW k&BL&|mLѡ!q-&,TF092xD]8mhn>ٔdgFSI$j4CKɏsc?\>:yy9KFf U^ۭd`3+bS P@Azv_v..6Iq63#7-"qX^C-&njnb={~5s1C&8{bwFXXv. uw(4.UkM IL>_ +ޭ#M)7>\dVBAI"Nį~J-Z]*nf\ذ6UH$qgB=ey nM(6S˾$@$`$Į]پ$4+L^>BV^'y )LJ.5RQ[P@W\+RU2۹GqgU,FA7=! }i/E'gվ_|j7jU?oqيM\ٝHXXtN`eMb.3Ա_X$@y/11IRg|֪Ez HZzԬB?+&K H eWװ1l7@ \"N$8T xko[j _:F4)5RCRjD]<l'Rs"q΃ªub^YP幤Z*f$@B'HJs..XSqq Z,pBo&իU1}mN *xO9~zha/L,Jk᭏;j~9&.<]z'*$`zA;&KHHgAa`|۶M3KTQjj"2aFQ.4sl0fg+KXʨxaŏtJ'Lyb InZEHMFr6vJ {oMT|5@#fUDڍS"JL ;)wGB(L)!fiwV*Oɍ($wpZuzN%6ڸJ^(ttsW5*ÖIwLMclD$@"Bg: ZHϟ0\*ʫ<=54LJJd(` ?xo\ļv9k2?&1/e݅K!,":˩0׾'mm4eh>ӮVGw쨅25K)%$P &yA٩$0ravH-۽ވp/-11 ^hG& 4iV]G4ɳFh\*"K#g3 PҀ͛+}(kx:;;+rbז->6i՝A:VmV+abu;¹{f/y&@ѻ*2;e ]ӼwJv['Ф ?HmYTXXf7k1/O?9k.Om {U0~⾻ޯU7kXU[MYzZFR:ȵceK(ZUӭ*RwRmCn3Q"U!kfŻ<oj~:yzϿhvN>붟0_ߓp;Ҭ(L$IRgk;cApPYRozj>|Zr"~n<%C^z8rol8Inu|3s\Z] -<ᚔKwh M4Jty0,>>~\W~%fVGp Lք ?~ trbRɗ?} Mf04kpQ vY ʪP߸6qiUV8En[fS}RàoEW3S|Jedk!Fٺ9Q~)'6٪tu -V;'D 8bQV 9p3q!AMw3 Vm|(s1WYYAwo9fݷE\n w9I(_EtX5 8$&ݯ1?I(:H~K_ذՌ:/!Sǿde|622]I*=(ʟJ(ccCJ*]"bO° pJ6o֒yХO&I8 ]氥g~{}t0zG[lW\\::رaTtmyLWc^CD6̡n c _ NRD%BDr ZP)R-PJ8]{Z!`f *a^+StvAGp>FKL<8ֶ1Wx]p AR50Eнۇף!v[ {ٮm݁ "dLO4cr[uLC&xZ_.N% nw1^EHr|oi=ohS"-]u!E_q”9&|Q9 y!Rwɢ[3DA``pοJUk#"&Mabq|/+1Sɿ8&</͛;v\lؑ ]ݵk{V>!!qŢUP]`h9Qz 8O6iժ) &z t9S8u릸.387.r O(G`Rݑ,Y9V۶+4481(UŮeQ;'>ˈ4g&!c/ ,`Ű|F'Sqc(ɟpBp0Kcn=OwPn;ݚ]7u`P$:67%Lc ʱf7"!_wI=;aTYl/2{Zr>yP LFy Q !=Oa0`h#;1P>d<(2t"(.5Ӕ1$ӀAcj8Eaf-WLIWzmĤ#C8ǽo[d\iO$y% X9q˲2 \^Cx_d溮ƚUʩd}͈M"nT̩o?2J%JfVV+EI3(>>}Ƕ45tt*X %$Ɔxi6d^V1UW_FM-ۍtT6Y@!Fy’a|XmEz '$F~3fG{"M4!qIW^/ܗ;n6uաm{qӖVUCrؐp\̋aR D _{HӦ [r]:hN6Q"?uԅ:򨫫{ڶmsj/<~|zzaQؿ_ODL4:M'[&,}=w)չVͪp8G*Ȑzt{VVY])qp:y"|d8]C&np"CY-^6"2'E#(xJR7)#J^v#w‘DX06+-M2$Dd@p{0k9K&Z5 DHI/Ӧfw(V"^#۪]P'cT߄p[44,瑪 w4  AjמO~$D"@ JDL˅5:|:HpF)Vѡ/"4-(1t^ XRbB D"@EN"K"@Se;L11L25n"@ D5"@@mj(jݽ% "@ D) ^#wʽ·xH" RT\s) D"Pb^l5-" OȞ''s6"@ DPW D"@ DJ৬u$7 D"@ D"P" ^Dn;-"@ D"@R Rl#-"@ D"@@$@zh"@ D"@ JAJ"@ D"@ %5JӢ D"@ D(k(6" D"@ DH(N&D"@ D"HH D"@ D"P" Uo˪[iIMHOUrTy4Y߲G)$  D"@ D\ ^#WDXuki#| Unn;E!D"@ D"+D"@ D"@(jT4? DB@ٔVSpgo^҇ D Ιܨa=uu>~ z!t[BšR"kQi:v#JQ| ]Wcͪz*eT2RDŜ ͈J|G %5DDyt[0  [FBczUfxO~(DI/yy]2i=oCv _>۷fW2v+^ZPСkU8/SL\\@ЃOO2[$Z6kڰjg Þ>:}߀< `jj|QVޒǎrj|ٜ|iզ7n9k^Ӧf&w7e5!'")q^CބK13UUH;&.4?bRY҈UL4Q_"@WHoD$ЦMY3&p311Хv>qLŔ_R5uu4ccCjT۠ܜ`Ell).Kr)&f Bc|LMT.L>S S q>*Ŕv-}Wcfo1iQ* p*b^^uWeG $ JBI' g8 ǎ㟞nii֮[+GP-77"(Na')ܹ+C WW`T<fW/dVTDž(˖o4=7(KB]|5S*&;a\xUG a46PpzXwcm~r[ LHM/E D[PXL)]5teI|ӂ\S0TJ*eu8=B-}(P"@ DLuքmsr,-^"֣{fM]5͛Uj0X0ryuVPdא :ro)I qM4DֈM$"@ JGI6nŷ'ϳA%s/66/\sиpZbbڵ%Dk"Sζ gY2S%iPX6Ҡ#5^sc~GD@ }66M[::iW*di !y=+ uԪ{3% %Ԩ!/_pj2,;طJm<+9%&GU,~9xBlS߃֮Рz53ii=QԪڭmv%Wr QZU{m 5˕Owg`7Zm7\:^`2Bvn\tLL%%"*6W^y+oֺIZ ~p&57Կl2Jm+Ԭ]n淌Ԅ,' g"#2m/v|*$Iڒ !$CçݺyA]%-YQ]mmex6OҍKl퀁=ŭ{ㆢ6#u8 yoݩS*+'$~{C'U[蒥2s3V4ڶu3{{7oߟ?| VUUΝ~䯾~W3)߿Fo[,(8JsHf"]@ МXb'CzA9⇪#۫n3c噾^1 7<~XzTH[tXAT!6A=8-R;>Nf/oNVFM}*RƸrA#J`evRLcz-dX%t߾OH!4>-=vcV&߼zXFHmp8l }aʥ_vBJ !qVշÊ# ҋD^g12K)iLqRB!?KrwI- B sYUs޻gxCl3۳{Er= 9!ИP@4lI#Mw됐c 'v!P2KsO&袐uhOQPKh/Rtn n\ʗϱH<4tHápFp,\c爕d4(P |i$l7`8蘚9u7ǼfSg>y5t>w~q߿g".'pXjy=˴$m}z #13B?r|H"DM[ԡS9KkXU>z }ZZPxpVs< :hมocԾsze d%HONtWfW]\cX5-K*,M{RlP RqS 8kl *8XI)L0&}x_;PTlЮ=(k\]n{ Ĩ$;!nڜ#&/29s͛ܖ8N2)ueA@۶M3XjàMbRAÆu:ut|ޜ?)O,GM׮EU?ai8{xԱA}Z.1h^X2޸y wGjogӵKU'R ХiH_KMMż;l"2yu۷mcB%1}tF;v@OiRB0Sv/DFEc #PIkhLĚe?r?\p;~_-bV.ȹ)<_[tM {x|;?аe ӧ>CGNRtpI(^#^kS*Ʉ[IOD@:\Tt}  HGzě@pԾ6o_v ?׈EsX1Pg\> ˈD a*wbO qh> ku0h\? wFࠐGibЧpeݰQ%g$3=iAwo >={Ŀ Q{r"e*]{#A.NG7u&h Ax$F6Y<ޱTV 5q킥P:AA5_N68[n{.Ɗ DPƽ>~0SʞKvqcmcED@ ?T|e%}G/Ɍ+ZX)4^z+ \XƇ'OG A1jt6/\?GnYq={t2))YtFxL1tHј6}O$00Is s<(/0cn7숊'zSWa$OOO0q y9R3I$5c j{tx /;N4GM>2.VzF+P>#߶eZ(ի32m*5ͲmW@|x1p.N(ATZWfO̾e[xpGXM;{V'o*5r=~7s$,0X)r)\Rj2Chz#nH놂EUDdhݺ魛n\;% .`UO/]8 [)*̋q3J Fs5m*0a%R'pm"ȕ6Ȭ]`cm_)C&KC _fX ;_ ܏`4G /XUj}Xf+D҂Ms%Q9SpIvhSg$o܁ڈys9lKYQDV@:5Wl_zY)[We)E5OdX4ٸd1⭫B܋(0em"$9)_}[@BmL ݛJg[4 -l( +BrW@ NC[dE@d9׭:Z=;CBl&BNkvc9!ԠfͪL N"ڝ)ٻ0~`~ ;{y8jٞk ÆO[A:_J4Oal!H?ƵqP~EPO墻#4JsHJD1C54K-/{"'Ĉx1s@<ېp40H^>)KwwZd qI(o 4`!Y X0Q$7ck^ } O٩S=5"N; lyࠁ)(D sL#A0nE?ǎԌ()00;&)7݇ 8|@&Y],Z".[N%zcH8iK$/U/_b]㊂DB!S Ϟ MttNQ!EKALF%,;| \NI#5 MNzTB=?k'HE"0_:D}3Q9?y* &8GRJ?f B(_M%πDdhglR!L 4st.|L_x^\s)OJ\S)ỗ d9ϥLE6sAD%_C`ņ;8-E .C0766Tk||&'-[6PK3;6q n3" K*%K g CN&LNn^;z쬨O.ӄdN{KOJאa!.@ ՊڪF+˵M(4M đ"tta_yE"@flHPLMRH7~.3"}K^fDğ:S$ _B#a.d aj9`%*'_$|`1LJpH]lRzY$) U"fff8~,X>/܀( ѦEƬrd̙= ;vJŋיa%}`jnbtc҂0~@%ȶ;,ZVVȠNY|\rF Be }_߀Q*q|5+$J|^~^#Ċ}ob}wW)e:)h"&& I?ͪ״jЈN]G-p ʊ  w)$T ).(4#OmQx(U6/Y<[OO.")( T<׮ƅ}>ldy+ Đ03pIh@U2'o$E]iF޾c՚4n DHn-9v\c,ʕ5WG 2V$;Ўn3cGQ44P`xr~aʸ}+DK_;}޺܌鳬 h+ Y$>q{9T|"D@J^/߶^:ϝs*&'r;T (%ٷw}EkK)W# {_0h0T `.bH)u/HQ\w0+53rLAPeqNrrqUD@ up߫#jֆ톡#Nԇ8۽=Ջx͇E)(MR1aN$lݪ)'$$*䲈̜Ԑ4tIR}e\\'+ ݇nkUG;[kkkKnZx`܄Y|(A')(r%hjf&e41N[?-:ӳ'u@gnYVM` alȴZ*_V0LL u]AiS=( 4mLQCOE"?9{gg+߻GѼ(0L@Dm&yTB"2M -}V }9.fX(2,իս">/M]]ַ I%ODItX .D6%Y?RE.@SBɢ`ȭlq$Z"@@FqgOY&\ G!kodk }Tv6g~e|t6B /Dz~Cv'RC%`9pJX> R Vxh*<$N8dkMcظyJ E#(8ڳݽhce*QSIe4WW)#Ij,->=c21C>~eZjU),/  n^"ocGXr% 'ms_: Rbr" iIعUfmnKwe>"5 ڰ!ZRrM"A*SĔ<Ǜo"I*"P Hjl" ȘhT+]Eέ3lܠHE/0Ș۶M3=g(4;azGl|Y m$WAjfܾ*}Rh{Az yN NfVQ/|Ӕ,]]LF:Ѷ !5VB_}0W-v.CGxZliIV. U5C\0n,:b@`>r˷m݃2?dVU yuDECWmfrBہ3| YWX#eh?=j>\4,u&FO`dz9)Mr9ld\Ww0@#:\*>WD@ <}/YHm_tQM7s<==sm4-6<)V8x+""穞( 8 A,SޤI53f.¿ wƍ̩}Ϟ\}6R|>ltOe:555޺SKK _Y> ϙ=*ћ%Sz6JS(zQ#"rmҪ@|(r̬0w63}+ǞP*#K\ 05`&ϯLqg?uаn*`P~D@ܼ4"8,ʪA>?&.$4naߪ-D}.#5++-vA̼5:-5!>2' 2ƱD|kOߒ/.ϘE8E D:fLF߾NbG"޽IKLT1rF%I=޺QH`+=<15 ՋA蘚Z7n|hA7ѵKY"r<3$vݷ탴^BT‰^Z0糫`AƜ4yqa.B oߋnڼU6faB]`8# KQz 8Zz L:t Cv7IMJڿ8&F;._6G)*Missӑ#~2+sATF\;1xaKóÂ|G,#*ǟPh̯ߠT>3{!,O`!+l0(<2t@xbl[7*DKNQF^+who{vڼRkgӋP$:67%Lc _ǎ:.!(S_=@}Ȅy@2tbjn;2bϟ<~-3s=knFW A_. ǯ\qQ&{ _d溮ƚUʩd}͈M"nT̩o?2J%JfVV+) E!՘ԙs>Yc[M::iG i !a/? ?b#{UתQ=s v#/ tPpQd-?%6VHݢv=; \Q#_z] Ǚ\gypV">r , pB 9rD& \2hl4tWI?*B* +7u!6\]Td_L2q8߽ bx*vEi˖oر@Nתh )`u@~=5mOl! td8̋T@o`C\uNp{+,,W'O]Ӎdl-n#IGM`(ša l<dr`hgХ.z{.^6"RRLEOš$\.LiMVB fFi 0e\֫ ges1 D@2xs{is;6KnOD"@KGX(߰Qtp"DO )(LNCR?y^aVO"PxvNQML3"@(.A,i#Z[[FvӴDҥ{n"C@J Z( D\>]F`wW\ƥj"@~)WnpZ  DP;U681"ie"5nh&!D" cx%ATL7kbV|e8O򾓤;+jI v-7P2{Kr%D"P kI^8j6T>^#wVʽ·xH" =ZOKa1"@ K ..~ɲugO^fc(n/KPHȞĨ~!{nzrBIB"@PDHh}}x䅤$Um*r>Gʉ D"@ DE'SOE#D"@ D"@5v D"@ D"@+kם# D"@ Dk=@ D"@ DW(;Gr"@ D"@ נ{"@ D"@(HQ\w&D"@ D"@HA D"@ D"P\ ^M D"@ D\ W,n,7!=n7>~ͯ*wu5֬RF%#.-?)ATЌTw\R 8N$_KW\_O ˁEo$4fWkk䇒++jP\8b\>iStlW9nǶO߾奯ڔ56<3ڋ{n^'i!@[ Ab@:umҠj'}eʔzSK;ԫW 덈jզ|ٜ9__`o5jjj|QfE;vXzVWiSG3w7eE@Y)d!wzmgUU5Τf FN*oמʉ DhӦ٬ jRwAvܸiwfffqYYّ+˛}YE%'ENM5qFlWRjp)pk몔VFѕQ{2Ӥ"GܪĈϸS9 DYrR+,>)λW4416N{cobVFv6fuZZyUmk3C}I/UD@ zqs_B2V!G' [QW~U=(%D(&L0??=#ܭ] wV**ُZ4o,@/>Q͕H!'J2\r"EVFɗ]-phm|@_19m~r[ L@\h.!)Z^#5!^ToPH>"@'#Y+vn޲m{ꭓ'/Y@SS~uɰ02g_0kz!> Ȏ.ھl]MG]u/*5r $VZU^G+[Ϡ!SH B] D"P2 ԭ[YWJ {/iVSSѽCdE&D(&kk_ŷOTШI FƄY#bd6) ԅ"@ %@مoܸ‰9]S9 DCjukmfh(fL2VQFCbJ`z!rG" @9K_տ]![x-4maU^_MK Ħ%G<}TJ\PV[)o .F Bl ơE=S:iII]啗YJڷjkդrU-}|G_87Hkx5ۘlZZ:6he6s<덍 5khЀ>(466[6-I׀_{73"?ݪ:߿瘱3||¤¢%@~(x9BN(rUGWf53}bnP*1=s+7*5k3oX-) #9E̱Ijo3C<_\:;n.xąHȻB-U54ܗd>}~*-1W]\O5-KV¢^c' kbZ6hخ}lp=;$ԡS9K/~^=VRuڸ9y):BpԸ["r2a o^'[ &:k4ʚ;+r%Q-D%D%F4 \H`Ǐ\9(ǎ{!)1kTCCì 1jT0k19s͛:eSr<&ANIy^l f!!;,55jUV /)G]} * 8[ؙ=Ŧ Ka cةӗ^|Z-ء-YVИ0;KKJBljkk O{x|-$C4,h\c],kWxǰْxxԱ ɓFk\xʺq.!~RUSڥ= D( lBDkmUE`6p+2 o O&C;<B\Tt ' Hq@ì}m"޾V+*XkO;fL8m?8?޾5aSH!t Lb4hKe_7.X u0AP- DYP0mbB_퉓{'7sEeZJj=aME=KKcE"(^?az䠼JP-LD&9Jכ|qw5l Foeݿشa%ص]=־|WizE%6 j{t#[_w8i.3#"|1_J8߶e48(ի3QTS͕Sa4o1&2#=S7qLy; .sV S`lK( RuaXt闃pСt`vіG(t ~C%D(֭޺yƵx "L1,-`KJ Q$&+r'*rx?#` dޗh`P\__`BzV#%5XA d4e|VNtXI7]6̞Ujcɓ-6q%lu;J f/^% "@Um6$!{>,=oʀ&D#=R2RSo,@  8`Ep@0g,zߪϞlHƅgJ6 IDAT(#Z3';tfhCȶE&˹nة j:ySX*Pd^pl݋,vu„U8N#HqᐔwVj"!!۹}|7KM5ۣ}-[=|(̞Ԋ9g.7۝-U94\_}&$B:5kVedto:{òCV8"@z Y,qb41 HϱT4oI^0X`!XzL+v/r < 4sT|̘1,P”feH%N1WDFW@hL8~*(MD"<2rU"'u+rJa*dիwGu1(9@ ҥ3P  TKSUTח B 9kٲٿ $څ1BU4'茰 +ZE%EHE?S{K~)PBhQ߅r*D:x+JxRk"PH^8RN2lT+h?_Ķ cr_}P_v&0##*b>TNϮ‡bޜɈn+xVzW^Cv$D 2V$;n3cym:(#D@`Mpaʸ}+rgϠ_;}޺,T?\i{cOl^ΒB*J&owNI"'#FN0A ։q󡴓Q"@@$ݻoݺ5jת^%A/>0K!͝3Uj}~ϞKcQXOffN|yH&LFZ 1^,dl=&ƉwSiD" +\̀Pd[VUHM` ˀ !jRGŦi fwoO-]ODHB=zY;V̓ }Gi66֭[ /_0htZZ7@$@`z:BaDІ dTs c%L)d0EV_JS-  EFqgOYZS(9|KKe??+-3פ9 'hh~gr,7d|k+)5K@zz35s ^S\ (8ڳ;{+[7i\`+5DS2Cd Ťfdgg&S,_*/V$(Ӥ1GvUHRm |OX@F񐴏_ZU5K/jCbzy]FbǑ&%&'9-^\jDzW#hE l8Ԅ\HJ5Q>|\]WOB-U"PbbٍM$YC " M 5Ӫ[v6#%15Nü޽0ڼT(R6ޑʋE(L̬BE4z򭨴fڴ|'Ȋ1SHzjS(=0ą0;ѿ1UG-dQksxtDxcyĽ`bRS(e2Y+7uK+.'D@>C-$b[u0,4j Gm-xI"kgHʵ[g9^_8u자(LJgxK!W;x` "q ̅EPx/x_>i $p}BB: ;,ޗZB -!Y)rjcsa?\VSckSb |3RS*<8#3򛊝qԕHIA4Mn9Ak(v:w쪡'q~p4/')2ju>aԴW+Z^[,* 'pcv#F [oP|0tkO2!0~Wv娅E1ZqJJO%5S9ب"B3flu&FO`dz9)J6 ,ī;x~ʉr/EkŖ x_!&9} S~96T">2553{"Nbg7LիoŶa F(nҤf~J-Z6l*Z`u(6WΘ2rB̩}Ϟ\}p6=ȨqĥU+WGQţ|(r۵̬0w63}+ǞP*#K\ 05`&ϯLqg?uаkn*`P~D@ weX&emlqMO:q1L  N"EcXN; pz҈lƪA>?&.$Wnaߪ-D}.s9++-vA̼5Z 6#LjB|D:f*FھNbwout*;q2q֍Bbׯ\ר3hW/"u`zwGr r}^Hųx8ml\[8ytέF*nq^vD@Y RM1Qa.N[nZISSkyYEHǧrP4i-&HO0DuAt ӕM<uO:JZ֪%[n:sIqݎt-\%'1w$S$G(1;w! 1Y4n%F!4{trђpƆvlԨ. 5p#͛MGPߝ)*Pɦ{ZjhXm&&&<׀-XqM Ҿ.^8Sto޼߱esD9NBL uv[/,7nmXS#z ^Q1xWfj jgQo<ࢸ8.EaR4EMhbK51&DI1޻& v"Q:!{.r-{??o͛Ý}o^.>rPF#rE< EKO⁘Pͺt:`: Pi}b+O-kbW佪H!ML2 \DF 8u\@3۴DmOOԜag+ -33͎\݁OwM{Iż!0 @@yx⤹O,A $ρ|{Z=8_o֥_vjurwPTZl{nBH"`;t릡PCgJ7bco¼!5 n?q,h׮E:5˸$&&F9{q^D!ޝ;9P:KSj[t_N|9plGC&kڷFwx:k5k=S11^sLaC+_2MMц5[ Mʠ͜9U˦4i Yw ΗP>b ͓pqvzA+qzPd [^~˚P̽`%_1+Xrt+hcJ6Z⌐؂3Ek6AGV%l_yda0!@@@@@@@ we%jQ!Qp~@C77GM+~7}      P ~C2P÷gx-q      r:5Wbgg[|ԪN@WGT;G^pVP3=:}[RY'US@^pڏ Orm      PIIɋg 5X      t0|/(;wHK{` Cb&W/Y\       Iy ;U@^Ca       `װȰiP5TF8      Iy ;U@^Ca       `װȰiP5TF8      Iy ;U@^Ca       `װȰiP5TF8      Iy ;U@^Ca       `װȰiP5TF8      Iy ;U@^Ca       `װȰiP5TF8      Iy ;U@^Ca       `װȰiP5TF8      Iy ;U@^Ca       `װȰiP5TF8      Iy ;U@^Ca       `װȰiP5TF8      Iy ;U@^Ca       `װȰiP5TF8      Iy ;U@^Ca       `װȰiP5TF8      Iy ;U@^Ca       `װȰiP5TF8      Iy ;U@^Ca       `װȰiP5TF8      Iy ;U@^Ca       `װȰiP5TF8      Iy ;U@^Ca       `װȰiP5TF8      Iy ;U@^Ca       `װȰiP[Ux'@@@^nk7/'r,W`flf#36+Ͱ@@uϭȔ'ώtz^^z[a;;SsRԃd. c`\d3js EM&p@LJѓV ١CG3 Vc&AAI)X"{P$lJ, IJ`URXIр-Ń8J5p;grV~뷋+V:!nj~[g]V#5{ؓ|hmS%fMCͩLSf6kamg|ݺa!6 g3RCЌC PP^J= {i;\*嬨ziQcbbr/OD IWo/ǎlee߮2VY4jp*CmXw ZTK H;͘WzRҺvc#dcpVO'2?o9<2r] IDAT2mNܮ#cgZ=|+scrw𸽳 y:Mb-䛱Eu_yShیT7'%گzנ a'F2Q (ARɒNdڣ%P;ZLIPLIc@>ڵsA //b&55ӱ3oݭ7_ϷS/5zXYk~0&X `[51=Ěb7p=M?00@@@2oqu8ɴ\QS?<77FnP)cLn5ze.d=vo~!*AI?uǏcΨH:6B,)N%*+!V #}(܈i QHh!b\z}EZÆVHL%IPL"Ea$ߢydJ&)P$%>y\FeWn~ŽNn.L`Ikr7r㚞 &1Q/lBϮX_UOwb E!V ɦ2w ;q_ӳ|Hq{tX$JL;Fc`^rrn H \(ZۿPyL` aK-v|aUfL6e:eUFJnbw ļqTS BPM3M}`b @@ KZ )1WED95L66@țl\d1 #twwqe(W 3Xb.d6wB2[+-șYIYJ3p (t X`&}SN&𪦆ܮgZ+ƙ3ƿ }|[*pc~=۴i^Vu:6))~dˁ;wx W!GN];UݍPC.iq~HH3oߗGwtG1eo!ӷ^f:O;KrMNI>sƿv<} ٌ`0;XdV@?6l]БÆO ^HeȐݻvVCBBҭ?z?ŽT(/bJ^^}MZ5zv5fDkt D{*{UhحReܜ쌔M31T޿{Of-]yɔiT=v8F-ֶպtkݮ\Net$!uܽ{2uPѱutj|[^ñYB'EE\#{FZmW:w5휜JXY顙)a{w.pGZMW3mkXS2U휝3(aoWnVW}i?y,fqM5JS`Xiߊolۗie}- %mkU{nvvrTT37,iXb3A& 3VM7诂BgZq n@iM'8SaVfɣgS>us+ŅP}5m8b;Zzz>Juzɼ(D9ч-?_zq}JNFQއ>~gh#,X}diWd\oՏ}|*pԱ }4cRBxXz!`ukPƇRiD3"ϞLgbjuQ (WAD rD'11k %2>S'c_LUk^ }%yzYT7%L+,۳WmZI <]_pV^/=!+VUN6:Be劅ܤOzd~㦂^Z-Z/Z__+leu˵+?JjhXAW|,>k%5v%K=/hՃ>%7`TYk%5 l~L~R7%Zۼ>WElZB+V\.NZ̥<&5Pʣ+WX",4cEXP֯b#gTe<1 5,z^CQg[q)c:5WiG;oY9^;i-_~1}ٺƏYfGBB NKM#I[B}5_Iis;w6z$tG6#[w@+5mx+5-ͣLV Iys&={+K]9$t ~BۯfL'bT mtwgddxӧktאBJL4z%" ޽sth;\\][:iw޺'=ZUh%ԅ,uL蠃{ݺzR,C{$@ J+ek7l>"ٳCݼB֮߭#s*Ifozړ8-c|h3v2!%ޏg`RM{55{Nn-WѩکkwGOr H]W Kj $IF35Ul8JOL&Oe5.^wUNb=Jt:3y ѵi1| JNR6(ڱ%̌Pk&H3!;OJ:JaUF^m2}f0zhMS4Y75[{#2X݀F~ǽX97LѡL?U@^üeUkQE C"&b$srsN$lٳf״ĺuдIWsSΝ5+3SRRat#7lFu;z}3+#;\$[[ ?tƘOq<[W`!(R [$ge*O6 LK{{%qyG "MJ0F x 'O]d{Xɓx I#O⢚`OP 5l:U2b'gr}]߾IU5B悾n i`Ҹ.$Qަ$~΀WUZuF%i胟Mx.S9c^JS8Z멒f|:]5C9k4Tq_1 ާL%qXM1jtUb}*q{׶e?0I%ZBMСEvum%wÚ@|:UĤf~7m`erGkPEln'^phdr[,YCzhOLѣt\n S&kз-bgW?q}Vܼ_O65fY&5XtWk{`Р>bgg׮mKdLEBMJδ SFzun#`]d U`Z]+QlM.E89w顱P1Oe˦?7nק+UHYFZbTcmէ$##8P}xON>O0UEhq7 lWd#?˻u&AІb9Ξe(G:87T7=f=*>%}rՒcIO2y .]ϐ.ECX(*;#yLrOGVn+]a>bhfik6m=/涂Z&=r1>,ThD몝ۢ/AD}K^&ЌX|h_ciI)ɇVBI QԼC,n@ oE ;K B nHթftlc!;U//FqV81pꚅץ  |f?UU`m< @3撊'/,R}'2L c @Yv>`Ǣ+PΚZ4 VM{6$=PѾaY/d Uv`ike*|9G_,*'Vt؜ RBZn!0hQ/z0](ťe0Ijzr/24;tt5Ef;S9Ƿo1*>%YiڳIOf=ĺ^آPbg 䋎 @^CWR+dd~-'xjzEgh+Ls8cOYK|ĩGՎ]_?q'2t&6ؼ5Yaʌ*:1:ZkF'zTlmEo<h"`wC=)]d蔉BL`sa B,ս#Gt zL@@U&CSџ-GjJƮh5md)nle&LbgV86w:6k$pdBWnxis{úzL"z_7ǵHj,FE2ٍ3/irTVsqE/Z^d=:BLؕsa B,՝%Gt zL@@5L9G 44b# 웑+o_iկQZ*~~~,ReMA-+GX1*NZϗswh#SIzx_Q氡SiW|WYB+5h cڤAbo\KON{\SiA3^Ś"¶}0@)5+Iol|ƴvãZu:;b.A+ϜAqMr3P> n@i Tb"i6:Ef'CP +Ǜ(999.\ӑt2 ʠ> evm߾]K:$ʋ<5L6؊Li tYEZw?VgЁ$: rNaqwn3"'ZnU?o^S;E)QۗշW6ZwSUdN+b_țzBPؼˣK%[ '}(uYzX8HKdgkj99:+WmޡǬ}3;D^UG9`-ޱɦV1d'q\Ίķy+K6oR#'35XTlBϿ(KR %YKlEߜIQw96(*ʋlEyJqV ;٪] 풘i/hrB,hE߀Zn*bde#tZY0 JJa-;85ٹ>\\ .C;K !1Ve>?e߮z<ZꎂrZciMŷotdI:9Ĺk̓ٻ*ɥiܤβ\5а1WιdI ?RaF2#%EX K./4 .{ {2BHd3b@] J>L;ÓH53^︍O O{[G'bCQ o~cQ D8#%5dt>^rLOInamEQjkTׯVa쥧O +y`#6JDxYFF>dSM4Kme=FFF1K4wL@r*ޑ5 5d%i^l52#h&4㱵 iprs4ih;w&4iV]Gu-{.՞([>%h(Ws512B1/*%|1F-o_z 4ESj={m)SEo&Ƚr5Vy| j|{'%6&UgF('|)]7qJlT^C}ƔeJ׽2{@ yQ}tX kPP(w.M@@[Ȓ5 x:T)Waa}W%QOy GEVrȡMY,]<>c?|>>ǎloh;;;jBb!eq{G\lKWzq&HnH/^ J1Jy4$-1֫n}}CP!#F黪۞`BUIdWه]Wk4fSSw+Uյi0СfJ34Φ+D;~6ۼi>r%-?dǵ>; {5"G}l1V~:b_ih0¥ZTٛ cL5{'F 7V!om}br2fn@i q̵l6DߎoK Ԕ}q!֧͘vq71~ݾ#G >zwjV.$-P[^޾i*U&OXwt֑S22^-̋Xw",$J)/R;eXRSӘ.m۶p4lKaa^Z643_}%bٳZ+I/sfO|ؑtsw\4*bq!ce`B wEϓpEBUV -["hKuR|~?yPGzhd%pw[h5u"(sRϷVʰt%{K?GN YcM(n 쌌{v &.:1׮.-Zk ms2 JTڃ>t%OB?i?y1-?cy[M9aulVJK_b-? 9zPK?88uieZ;9n"!ޡJM&j!/̦'Ά$b7 A>~}U(SտgtJ2 ?yYBO1n@icum IDATZ}=<nj~خm.]I8T1d,`.kѣ{GzO 6I{ xչsΝڲ6ѓ'ɯaT+iitsZ$Q"lWg+>7m\{!8$ɦTjq?\/R~yFB`˖h޼Q@#Z&9zbQԠmZݽa,{ܨfY2 k!aĹ#ӔWV1M JuA/w} PB>f-5hNB 9V7F!9YG~۵+k~:y|<j8y&ڒzRXjW֬kW2)^jvj l¥_V{֨Y/.cxM^ %Pi˶޽V߁t Ёo8+t%V~ofۻ? )ݸ)E$BkUnn٨N>~Ly j'=IcLZy(4(R(wX '[!V|O6-vl5Sٙ+С0U;ueI_HIM6VK|pRB+*#6RB$KWg+;:kM{qennMgP:R4USn~CsB>BBʲmQT 5cUvJU`d/5w,MogG_;4(]HHt[q7];$b܀FUuK֩]رS^V5nwd bϮL+֮Z oɅJ?LKOMJn(Z8Tw>rUIj!|lYtEHhAz7)ţۮB`|DDdM~q1uҽ ._]hw \ަؠW ^9`y )+fӽ*>6Ϧ~y[xr'ώd>1մSuwŽ;23#]5eI<|U#-Ճ~Ub~fw2T҂Z8ӵj؆otl{(j}iɟ ^zn;nx-#}1xpEG[)S~*gGʼn\y&,]$_BXvVaVO'2?wiJWב jnmqm ;w 3OPm$;;4C\[v}aF m;n2׈<{JUgn@ $ 16ԍBഉTbE݀REGK5ko۲V¯۰am"@@mPWl3iܤZZrrr~m) IjPe߮ޱcGC.IĐ Uǎɖ(T׍ z䥷Z)I <ǣ?{(NxP*Ӫu˿[#,\!M#ʼwąXfQ '}Kw'I^O.N#- {}X~~ }ZqwnK)F 8u\@om}I xsSDZuUs?,?,݇U]ajkNKBMj\?e p巟ϯ^{UQ;FN6Cl+ L|`8}iQƌw{9huAiOߏ͝n<ѧHZNwhڐ;-yHQޫFMU (W$!cg ;t IjPE)]Tq7T)3͗N:G/Vlx-(SzF W/_.($bЈ6`kkC)whŬ >a`RF'r冃3)Hc؂oZfKD .Qcv>M<]P'$=ގaK6-Uaݻeܦ[Y޲u7ixD@ff={Ϟc'y m64iЪUSF~Ӧ J*$.rLV kYu5Z:i m,҈Nv9Y[AENougSbR$/'ɉvphVwI,wmK{Dۤ)*@v限Y4pz'߭'-h uŧOZ [Bfn|Zz0Jnk@VѻhyA{Ž}vK*L@x,W<:NM^.(J-0}"oؒm`oUFrғ Z0rE7mzγÔ;4m`I 'Aw?sʏ##I&ꓤ[jv]u5=YݻR WB&n s{«|x4ybcKSfW>Ճ/1}w[WxPmbG*DjG{b6SaF}h,zeBB PČe$w_*ѽCg(GtO{b|ޑ$J/cɓ|+z{+kmmuvgV-4F3jP>@@ '׺]2JeVR9H)#/zj$":KQ E%V qinia;5XbF4l> CgۚLŎ>FX| #X@q!@GVj9hI=:R\+s&#f2!6y 54kXZ/@=yk(5>dģ(B,A@<C=A@E]Ǜr̠qc!`h0~^> V/@P@       >B a2@@@@@@@@bkH @@@@@@@LFy @       @^CbP       `2k 5:@^d1א(ԁ&C@@@@@@@$&@@@@@@@@d0j        15$ u       &#Pc  !1PP,yo޴a:5HʿFcEsUk;ct}hXXf3͸ZX[YCYnemr]O0=8,VaPZɥuY;o'6Z$vM>m9wwdKݫ+;ӧ ]c2$oڶ[MRRrXs.uɓxIF1D$(G!.w_7wdƋ -;xUؑXksٷL3L(A-)X%ھ l:p͠}yW5po#y(գ֎ IS%kUZN7<#wu0kO2\˕OM?mrssvJKDn\JOfQ%11F V0ϰxhp[ZI -IڦKR?ZUFHjhx866N?E]y!~VRk%};ZvI,QEZfL={.8    &Y1κەkKyџyv#,|paN@5i.^$r%IٺZ&xc._.0⃷igff5i֍~Iҧw~zԩ]T)Ԩ3g.lk9 Ǣ3QO9}悮 AԁWf.}p欅\1|FbD@y ~>>mf BfeM5{﴿`q@C;;Bsۭ[(_]Oz0e/փJ#0~H6A5mwnp)ӭA:O SΝL; 慮=ؚ7w2=+%P3gbʴP GW)+4us,{NJΦ&5;Eusgex{{uК2&L'bu22c%Iҽ[ZAсR<ʔiժIQ͙|%Yg>c%&P[^hT 'z"#"-'["iIRHB87-v9;ҁkgͣ =o9\k<A\L9PSܘcaJʼXݕR?\~R`8k\gi0^B hd3}ˈ6kYK+5>1h|6 zDdtFDD$'O]d=CR=W\K'rqqqqOeXbz%Ts綌))C-za6*B,8׺7ļ2O{4~u#LfNcaa7&mTDcQ̥ܩSs HePlٳf״!ĺuдIW^a|rxKx)hkk3aG4Jm<[WҰO3j/BD߱F:  &#P\Z{_!!]c ^O55Dw?Mj0}}D17}1Y |-)p9ξ3}L;%LPt TS<(gol.x *0{"6NZ;wj'tj-MfX׮OϱS&kބlh$,J*0oɣݻ&5O]JR\QYfMF ރ` [5ew~^IHhc KKrBKhtLnR 3+V}V2cA@LI@m5[zj啈>i矦0 v*/A9&+LRfTbeoMIb?HJ1\8I@IoMk\M,u[:R pI(g?٢EnDhqPc] \\Ћk8 ^[a ThwѼa*^_w:H%(.,55322qhQk~ٝyhY }GÙq'M^6e˦ύ)< =W_>٬Y~+X9h!ھc|<SY3LI]/+;V@@4Զ^þeVs3M n]zK[ )?9^l0M<%yNĈ TY#/xcheN*CD&3 []ZOۅ~|YI(gr4̫s}':VdvzxaƢ]0eʸl\݁$,J !U^U׵ҹS[*KXuKTR8:tj9&61>M(/6^H+'ʱD䅋W޸yJʼnr/);V/  `zjkؔ-7M e@[Nh=A>e)#R+NP)nSf ښQmK2$UH?[PȱVI(gu7̥dOVVֳgޥ:yQJ:=ҪrkPҲÇFɋD牪'aUpUMLØ+Sb:`I,1 ƿvI =kq^z#(8,"U ^DSNF,+^k1asLժܼ~@bL_VX9!V%Z$`׬BΙ60Fbf;N4(RQ9dcwڸڹv(G)''lԔzccbVew1fԡz/S2$'(1N -i٥J|jNI,1nhܸ^6-VTCaIOStͶ=JÆui ѓt*hUjꨜ+{!.I0 5&?1Uml?*PՅ>ëoyV ~l'싮K (+;kOkDNH{e$D%kLB }4P "%RBo? 2%jJ!-[,^Bg!hL{WδexؔbX"-ahPm^##45dif%rt}xɦehc \3k#_U߰Wtj ;3ܺv%*t䪁BEM;[`Z _oiڴAFkԨV/j`]|-(3n,rnHMєP1&bXb O@ ڼV5*Tk3Sh:Z[W,ܬ+%j (յk)̹~gF>KZEDeJꗦCIt֢yb9Mm@NN΅ WxC : }eP4vھ}t=>ur4錂s ++]JhH@m硰(q-7/~Z¶aCҩ"lKPïes%ʿ\8Xt eRC]Fɋ'&lRX\LѫPl>[BѬȟ$(Gyz~?2jCYfVW.pIBٚ_NeU)'ʱpy::s|l;<@@@@ϖ3A ;OG:CJ+:~ue%}2ZiEMݻ!]h*DKn#{$kK%KMWգh͛6:mNھ1d`TC:2!3,ϦM c+! r,v0.WժU)Q8<˶?}+#GYn@9N -A%!ʎ8_v~] xzxϨkߺM<.k|M>-y%‡4i'Ewpnz *+#G#L;9ֶ߄ODVe^/'eLCzP2vOߔ]O!%?C[x2CsS/7ZBܞJbu(Q 2`Fp mx9zϩK*׸6.ĐV@Wuev4:{v=R 6mj_I^#r|t^ GLC k-սSWխYPvcNBX%Gh%55J ݽaZٹQz |מC|8:UZgq=-)ڴq=v%s9VXLwҼY[v )[֣yF}{|TT?j#ܑN5kѽ#f"3ܤq={GJyɦ%@ɎqʮBuJx]W @s}^DBLM!-;PBL#L^K†6A:bXlc%3O #O@+'NL'j@WOYfJK$33k?y6E_~Ho݅ي+_u]gժU0#-hEV^(ȝu֜_w / *0jg/- I9BLjE#%4LH![p9իEޡ)צ$o (9R(bAE0]'ENB%3dņ~,P_zP͕ Ti{J0DKf)1@ †'L5a%NHJ$}B@`U޲&Zz |&gB_;MaY"9&>nKb(K_ҥPc E'NSFc¤9ݵ͛wS`(jTUѣ8asrsUc P!۱f2 aEQ.!#"""y5KbE;rXl3iܤd}#SF˘v܀C_(T^ Fq]ەuSڶ=-IʌH}хH=48m(ɋM~ A;O{v9h'ʮQLSN0*JqkڸJyd<~#=g#7Ťz>ץMY¢QV$lY Uƭs ֮vt.lVLzՄϮ@% 'rL6]Hbv,\p}_[H t؊>rɳTb]u,fccq];ur9sx\hO{ڴiN[)RJNοq.]}ǾhPrP łP        ZԹakXB#+K %D>: θ+kXB#+K %D>: θ+kXB#+K %D>: θ+kXB#+K %D>: θ+kXB#+K %D>: θ+kXB#+K %D>: θ+kXB#+K %D>: θ+kXB#+K %D>: θ+kXB#+K %D>: θ+kXB#+K %D>: θ+kXB#+K %D>: θ+kXB#+K %D>: θ+kXB#+K %D>: θ+kXB#o T?IENDB`alcotest-1.7.0/.ocamlformat000066400000000000000000000002151437617477400156640ustar00rootroot00000000000000version = 0.24.1 profile = conventional ocaml-version = 4.05.0 module-item-spacing = compact break-infix = fit-or-vertical parse-docstrings alcotest-1.7.0/CHANGES.md000066400000000000000000000332531437617477400147610ustar00rootroot00000000000000### 1.7.0 (2023-02-24) - compile with MSVC (#369, @jonahbeckfordm review by @TheLortex and @MisterDA) - Allow skipping a test case from inside the test case (#368, @apeschar) - Fix compilation on bytecode architectures (#335, @glondu) - Get `alcotest_stubs.c` to compile with MSVC (#369, @jonahbeckford) - Try automatically reporting the location of calls to Alcotest.check. (#366, @MisterDA, review by @TheLortex) - Detect that Alcotest is running in CI and change output accordingly. (#364, @MisterDA) - Upgrade to `dune >= 3.0`. (#360, @MisterDA) ### 1.6.0 (2022-06-24) - Fix a bug when running test concurrently. Alcotest could fail to output the content of the log file. (#353, @hhugo) - Require Cmdliner.1.1.0. (#339, @MisterDA) - Upgrade to `async>=v0.15.0` (#352, @crackcomm) ### 1.5.0 (2021-10-09) - Make Alcotest compatible with `js_of_ocaml.3.11.0`. Users can depend on the new virtual `alcotest-js` Opam library to pick up the right `js_of_ocaml` version automatically. (#326 #328, @hhugo @smorimoto) - Record exception backtraces during test suite runs by default. This behaviour can be disabled by passing `~record_backtrace:false` to `Alcotest.run`. (#317, @CraigFe) - Generate shorter unique identifiers for test runs (8-character alphanumeric, rather than a full 128-bit UUID). (#304, @CraigFe) - Change `Alcotest.{char,string}` pretty-printers to use OCaml syntax on assertion failures (i.e. wrap with quotes and escape control characters). (#318, @CraigFe) - Fix process for getting the width of attached terminals on MacOS. Previously, a terminal width of 80 columns was assumed. (#325, @CraigFe) - Fix parsing of test filter ranges to allow '-' separators (e.g. `test alpha 1-4`), as advertised in the manpage. The previously-used '..' separator is also supported. (#312, @CraigFe) - Introduce an `Alcotest.V1` module that aliases the existing `Alcotest` API and provides a stability guarantee over major version changes. Similar versioned aliases also exist for the backends: `Alcotest_{async,lwt}.V1`. (#306, @CraigFe) - Change the `~filter` argument to `Alcotest.run` to be a predicate over tests. (#305, @CraigFe) - Renamed / removed some less frequently used modules used by the test backends: - `Alcotest.Unix` -> `Alcotest.Unix_platform` - `Alcotest_engine.{Cli,Core,Test}` -> `Alcotest_engine.V1.{Cli,Core,Test}` - `Alcotest.{Cli,Core}` are now gone. Use `Alcotest_engine.V1.{Cli,Core}.Make (Alcotest.Unix_platform)` instead. (#306 #309, @CraigFe) - Avoid exporting `list_tests` in the main test APIs (`Alcotest{,_lwt,_async}`). Use `Alcotest_engine` directly if you want this function. (#310, @CraigFe) ### 1.4.0 (2021-04-15) - Add `?here` and `?pos` arguments to the test assertion functions. These can be used to pass information about the location of the call-site, which is displayed in failing test output. (#291, @CraigFe) - Add a pretty-printer for the exception raised by `Alcotest.check` and related functions. This allows them to be used outside of an Alcotest test runner for making general assertions. (#296, @CraigFe) - Add `--bail` option (and corresponding `ALCOTEST_BAIL` environment variable), which causes Alcotest to terminate after the first test failure. (#298, @CraigFe) ### 1.3.0 (2021-02-16) - Add `Alcotest.triple` for testing 3-tuples. (#288, @sheepduke) - Correctly report test suite duration with millisecond precision. (#286, @CraigFe) - Improve pretty-printing of results to consider the terminal width, fixing several display issues due to line wrapping in small terminals. (#282, @CraigFe) ### 1.2.3 (2020-09-07) - Require Dune 2.2. (#274, @CraigFe) - Fix a bug in the handling of the `~and_exit:false` option when the test suite fails. (#271, @CraigFe) ### 1.2.2 (2020-08-26) - Fail gracefully when the user supplies an empty suite name. (#265, @CraigFe) - Fix compatibility with `fmt.0.8.8+dune` by adding a missing `fmt` dependency in `alcotest`'s dune file (#266, @NathanReb) - Only show "in progress" lines when writing to a TTY. (#267, @CraigFe) ### 1.2.1 (2020-07-15) - Surround pretty-printed diffs with quotes to make trailing whitespace more obvious. (#261, @CraigFe) - Allow `.` characters to appear unescaped in symlinks and test directories. (#259, @CraigFe) ### 1.2.0 (2020-07-13) - Add an `alcotest-mirage` package, allowing the construction of MirageOS unikernels that run Alcotest test suites. (#238, @hannesm @linse) - Add `Alcotest.check'`, a variant of `Alcotest.check` with labeled arguments. (#239, @hartmut27) - Add a testable for the `bytes` type. (#253, @mefyl) - Many assorted improvements to Alcotest output formatting. (#246, @CraigFe) - Default to `--color=always` when running inside Dune (#242, @CraigFe). The value can be overridden by setting the `ALCOTEST_COLOR` variable in a `dune` file, for example: ```dune (env (_ (env-vars (ALCOTEST_COLOR auto)))) ``` - Support all UTF-8 characters in test names and suite names, by normalising them for file-system interactions. (#249, @gs0510; #246, @CraigFe) - Fix various crashes when using non-filesystem-safe characters in test suite names (these break Alcotest when attempting to generate a corresponding log file). (#241, @mefyl; #246 @CraigFe) ### 1.1.0 (2020-04-03) - Fix handling of CLI options for `Alcotest_{async,lwt}.run`. (#222, @CraigFe) - Fix interleaving of ASSERT outputs with the other test code, and ensure that it is correctly captured in log files. (#215 #228, @icristescu @CraigFe) - Don't raise Test_exception on Cmdliner parse failure (#234, @CraigFe) ### 1.0.1 (2020-02-12) - Add support for an `ALCOTEST_COLOR={auto,always,never}` environment variable to control the colorization of terminal output. (#209, @mjambon) - Fix handling of exceptions in `Alcotest_{async,lwt}`. (#212, @CraigFe @talex5) ### 1.0.0 (2020-01-14) - Require OCaml 4.03. (#159, @hannesm) - Change `Alcotest_{async,lwt}.test_case` to return monadic values. These must be run with the new `Alcotest_{async,lwt}.run` functions. See [examples/lwt/test.ml](https://github.com/mirage/alcotest/blob/878c500f3b25f6ebbdafc7236ebc2b7756dafe9d/examples/lwt/test.ml#L85) for an example of the new API. (#167, @CraigFe) - Add generation of `latest` symlinks in the `_test` directory which point to the most recent test output directory. (#155, @cfcs) - Allow all CLI options to be passed directly to `Alcotest.{run,run_with_args}` without use of the `argv` parameter. (#182, @CraigFe) - Add `--compact` option for more concise result reporting. (#149, @andersfugmann) - Add `--tail-errors` option for limiting the size of error logs printed to standard output. (#200, @mjambon) - Add support for executing subsets of tests via the `test` subcommand. (#158, @CraigFe) - Change the `float` check to include equality of `isNaN` and infinities. See [examples/floats.ml](https://github.com/mirage/alcotest/blob/47908b1f576d65f4418bcf778d2a1db213f5a7ff/examples/floats.ml) for demonstrations of the new semantics. (#152, @psafont) - Reject test suites with colliding output directories. (#176, @CraigFe) - Restrict the set of characters allowed in test names to alphanumerics, hyphens, underscores and spaces. (#161, @CraigFe) - Fix a race condition on creating output directories. (#150, @edwintorok) - Report test suite durations in real time rather than system time. (#162, @CraigFe) - Improve documentation of the `?argv` parameter. (#164, @ian-barnes) - Remove version number from test binary help pages. (#186, @CraigFe) - Remove dependency on `tput`. (#189, @samoht) - Remove result dependency. (#159, @hannesm) - Remove uses of deprecated `Pervasives.compare`. (#173, @CraigFe) ### 0.8.5 (2018-12-11) - Port build to Dune from jbuilder (#139 @samoht) - Fix output path on Windows/Cygwin (#141 @kkirstein) - Switch opam metadata to 2.0 format (#144 @samoht) - Add nice screenshots to the README (#143 @rizo) - Fix ocamldoc headings to work with odoc (#145 @avsm) - Do not test on Debian-unstable, add Fedora (#145 @avsm) ### 0.8.4 (2018-10-17) - Improve documentation for speed and tests (#129, @raphael-proust) - Improve documentation on test case filtering and flush error formatter on exit (#133, @edwintorok) - Create a fresh log sub-dir for every run (#125, @m-harrison) - Fix wrong location hint for test files when using dune (#135, @m-harrison) ### 0.8.3 (2018-03-25) - Show one failure when multiple tests fail (#117, @aantron) ### 0.8.2 (2017-08-21) - add `Async` support: there is a new `alcotest-async` package containing an `Alcotest_async` module (#104, @rgrinberg) ### 0.8.1 (2017-08-03) - Add `failf` (#105, @hcarty) - Relax the `float` combinator to compare its epsilon using `<=` instead of `<`. This allows to use `float 0.` for "exact" float comparison (#107, @samoht, @talex5) - Fix outdated displayed information when using `--verbose`. Be clearer that no new output logs are actually created and do not try to display outdated information (#108, @samoht) ### 0.8.0 (2017-06-22) - Format "got" and "expected" values in the same way (#86, @talex5) - Change the `float` combinator to take a mandatory 'epsilon' parameter (#89, @superbobry) - Switch to jbuilder (#92, @rgrinberg) - Add a `test_case` function (#94, @samoht) - Add an `alcotest-lwt` package, containing an `Alcotet_lwt` module with an `Alcotest_lwt.test_case` function to better deal with lwt tests (#94, @talex5, @samoht) - Add `Alcotest.neg` to negate test results (#95, @samoht) - Change the `test_case` type from `unit -> unit` to `'a -> unit`. The `'a` parameter can be built using as a `Cmdliner` term using the new `run_with_args` function. This is useful to configure the tests using the CLI (#96, @samoht) ### 0.7.2 (2016-11-10) - Clean up handling of env variables (#83, @samoht) ### 0.7.1 (2016-11-03) - Store tests output to `_build/_tests` by default (#77, @pqwy) ### 0.7.0 (2016-10-25) - Add a `unit` testable (useful for functions with side-effects) (#79, @avsm) - Add a `testable` combinator to easily build `'a testable` values (#75, @pqwy) - Add `pp` and `equal` to extract the pretty-printer and equality functions from an `'a testable` (#75, @pqwy) - Add an `array` testable (#75, @pqwy) ### 0.6.0 (2016-06-28) - Add int32,int64,float testables (#71, @hcarty) ### 0.5.0 (2016-06-27) * Use `topkg` (#68, @samoht) * Add `Alcotest.reject` to always fail tests (#64, @talex5) * Fix pretty-printing of `Alcotest.list` (#53, #65, @talex5) * Add an `argv` optional argument to `run` to use custom command-line arguments (#63, @dinosaure) * Fix typo in JSON output (#67, @fxfactorial) * Use `Astring` for the unit tests (#62, @hannesm) ### 0.4.11 (2016-05-11) * Fix regression introduced in 0.4.8 about alignment of [ERROR] (#60, @samoht) ### 0.4.10 (2016-05-03) * Fix support for 4.03 (#58, by @hannesm) ### 0.4.9 (2016-02-25) * Add `Alcotest.pass` a testable combinator which always pass (#50, @seliopou) * Fix `index out of bounds` for empty test doc string (#51, @dariusf) * Display the log directory (@samoht) * Add missing newline in display header (#53, #54, @samoht) * Add a `--color` flag to tweak color usage on the command-line and use `Fmt` (#52, #55, @samoht) ### 0.4.8 (2016-03-12) * Fix `check_raises` (#48, by @yallop) * Use Astring (this drops support for 4.00) (@samoht) * Simplify the build system (@samoht) ### 0.4.7 (2016-02-22) * Minimal fix to ensure windows support (#46, by @samoht) ### 0.4.6 (2015-12-29) * Add missing newline to verbose output (#36, by @seliopou) * Result: add Result.result combinator (#37, by @seliopou) * When redirecting stdout/stderr, use a single fd to share the seek offset (#39, by @dsheets) * If redirecting output, print error results as well (#39, by @dsheets) ### 0.4.5 (2015-09.16) * Add boolean assert: `Alcotest.bool` (#33, by @zjhmale) * Add sorted list assert: `Alcotest.slist` (#34, by @samoht) * Add pair assert: `Alcotest.pair` (#34, by @samoht) * Add simple assert, built using `Pervasive.compare` and a pretty-printing function: `Alcotest.of_pp` (#34, by @samoht) ### 0.4.4 (2015-07-31) * Fix of the format of log filenames * Fix a regression in 0.4.* which were hiding error messages when using wrong command-line arguments ### 0.4.3 (2015-07-22) * Flush formatter for `Alcotest.check` (#27, by @edwintorok) * Handle UTF8 for test documentation strings (#5) ### 0.4.2 (2015-07-03) * Improve the result outputs ### 0.4.1 (2015-07-03) * Fix regression introduced in 0.4.0: display the error if there is only one error * Add a testable combinator for options. ### 0.4.0 (2015-06-29) * Simplify the use of the library by removing global states -- now calling the `run` function multiple times is much more consistent. * Remove the direct dependency to `OUnit`. Programs using `OUnit` and `Alcotest` should continue to work. * Add a `TESTABLE` signature and a `check` function to check invariants in the tested libraries. ### 0.3.3 (2015-06-19) * Control `--show-errors` using the ALCOTEST_SHOW_ERRORS env variable (#9) * Add an `and_exit` optional argument to `Alcotest.run` to control the exit behavior of the main test function (#4) * Fix the output of `--version` * Add a `--json` argument to show the test results as a JSON object (#14, by @eowzukw) * Expose `Alcotest.result` to turn a test into a result ### 0.3.2: (2015-06-08) * Do not fail if the output file does not exist * Add a simple example (#10, by @leowzukw) * Add a logo (#12, by @leowzukw) ### 0.3.1 (2015-04-18) * Fix OCaml 4.01.0 and earlier support (regressed in 0.3.0). * Add Travis CI tests. ### 0.3.0 (2015-04-13) * Fix backtrace handling (#2 by @dsheets) * Use `Bytes` module instead of `String` ### 0.2.0 (2012-12-19) * Fix issues with redirections * Display the full errors when only one test is selected ### 0.1.0 (2012-12-12) * Initial release alcotest-1.7.0/CONTRIBUTING.md000066400000000000000000000105431437617477400156150ustar00rootroot00000000000000# Contributing to Alcotest ## Setting up your working environment To clone the project's sources and install its various dependencies, run: ```sh git clone https://github.com/mirage/alcotest.git # Get the repository cd alcotest opam switch create ./ ocaml-base-compiler.4.10.0 # OPTIONAL: install a project-local Opam switch opam install -t --deps-only . # Install regular and test dependencies ``` Optionally, install `js_of_ocaml-compiler` and [nodejs](https://nodejs.org/en/download/package-manager/) to be able run the testsuite using `js_of_ocaml`. ```sh opam install -t js_of_ocaml-compiler ``` Now you can work on the project using the standard Dune commands: ```sh dune build # Build all libraries, executables and tests dune test # Run the test-suite dune build @runtest-js # Run the test-suite on nodejs (using js_of_ocaml) dune clean # Delete all artefacts ``` ## Project structure The high-level project structure is as follows: ```sh ├── src/ │   ├── alcotest-engine/ │   │   ├── core.ml # The main test runner, parameterised over... │   │   ├── monad.ml # ... a concurrency monad │   │   ├── platform.ml # ... a platform implementation / OS bindings │ │ │ │   │   ├── cli.ml # Extends `core.ml` with a command-line API │ │ │ │   │   ├── pp.ml # Pretty-printers for Alcotest output │   │   ├── test.ml # Combinators for test assertions (with `Alcotest.check`) │   │   └── utils.ml # Standard-library extension │ │ │ │ # Specific backends: │   ├── alcotest/ # - Unix-specific API (compatible with js_of_ocaml) │   ├── alcotest-async/ # - Extends `alcotest/` for test-suites using Async concurrency │   ├── alcotest-lwt/ # - Extends `alcotest/` for test-suites using Lwt concurrency │   └── alcotest-mirage/ # - MirageOS-specific API │ ├── test/ # Project tests (see '§ Expect tests' below) │ ├── dune-project # Project metadata (opam files are generated from ├── alcotest-async.opam # data in the `dune-project` file by running ├── alcotest-lwt.opam # `dune build @install`). ├── alcotest-mirage.opam ├── alcotest.opam │ ├── alcotest-help.txt # Manpage output (generated by `dune test --auto-promote`) ├── dune │ ├── CHANGES.md # All user-facing changes get an entry here └── README.md ``` ## Expect tests The repository contains a number of 'expect' tests in https://github.com/mirage/alcotest/tree/main/test/e2e. These are short, self-contained usages of the Alcotest API, with the corresponding output snapshotted in an `.expected` file: ```sh test/e2e/alcotest/passing/ ├── basic.ml # The test case ├── basic.expected # The expected output of the test case └── basic.opts # OPTIONAL: command-line options to pass to the test ``` If you change the Alcotest output in some way, you will likely need to update the expect test snapshots: 1. run `dune test` to get a list of diffs to the tests, 1. check that the diffs are as expected, 1. run `dune promote` to update the snapshots in the repository. To add a new test, create a new `foo.{ml,expected}` pair in one of the test directories and run `dune test --auto-promote` to automatically generate the Dune rules necessary to run your test. ### Random output in tests Some sections of the Alcotest output are not portable / reproducible under test, for instance: - the time taken to run a particular test; - randomly-generated unique identifiers; - the full path to the repository on disk; - the executable file extension. When running an expect test, these portions of the output are sanitized by [`strip_randomness.ml`](./test/e2e/strip_randomness.ml) before being compared to the `.expected` file, resulting in files with portable placeholders: ``` The full test results are available in `/_build/_tests/`. Test Successful in s. 4 tests run. ``` If your change adds non-determinism to the Alcotest output, you may need to alter `strip_randomness.ml` to get it past the CI. alcotest-1.7.0/LICENSE.md000066400000000000000000000013711437617477400147670ustar00rootroot00000000000000Copyright (c) 2013-2016 Thomas Gazagnaire Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. alcotest-1.7.0/Makefile000066400000000000000000000002441437617477400150210ustar00rootroot00000000000000.PHONY: all test clean all: dune build test: dune build @runtest test-js: dune build @runtest-js clean: dune clean format: dune build --auto-promote @fmt alcotest-1.7.0/README.md000066400000000000000000000210441437617477400146410ustar00rootroot00000000000000

A lightweight and colourful test framework.


Alcotest exposes a simple interface to perform unit tests. It exposes a simple `TESTABLE` module type, a `check` function to assert test predicates and a `run` function to perform a list of `unit -> unit` test callbacks. Alcotest provides a quiet and colorful output where only faulty runs are fully displayed at the end of the run (with the full logs ready to inspect), with a simple (yet expressive) query language to select the tests to run. See [the manpage](./alcotest-help.txt) for details. The API documentation can be found [here][docs]. For information on contributing to Alcotest, see [CONTRIBUTING.md](./CONTRIBUTING.md). [![OCaml-CI Build Status](https://img.shields.io/endpoint?url=https://ci.ocamllabs.io/badge/mirage/alcotest/main&logo=ocaml)](https://ci.ocamllabs.io/github/mirage/alcotest) [![Alcotest Documentation](https://img.shields.io/badge/doc-online-blue.svg)][docs] [docs]: https://mirage.github.io/alcotest/alcotest/Alcotest/index.html
### Examples A simple example (taken from `examples/simple.ml`):

Generated by the following test suite specification: ```ocaml (* Build with `ocamlbuild -pkg alcotest simple.byte` *) (* A module with functions to test *) module To_test = struct let lowercase = String.lowercase_ascii let capitalize = String.capitalize_ascii let str_concat = String.concat "" let list_concat = List.append end (* The tests *) let test_lowercase () = Alcotest.(check string) "same string" "hello!" (To_test.lowercase "hELLO!") let test_capitalize () = Alcotest.(check string) "same string" "World." (To_test.capitalize "world.") let test_str_concat () = Alcotest.(check string) "same string" "foobar" (To_test.str_concat ["foo"; "bar"]) let test_list_concat () = Alcotest.(check (list int)) "same lists" [1; 2; 3] (To_test.list_concat [1] [2; 3]) (* Run it *) let () = let open Alcotest in run "Utils" [ "string-case", [ test_case "Lower case" `Quick test_lowercase; test_case "Capitalization" `Quick test_capitalize; ]; "string-concat", [ test_case "String mashing" `Quick test_str_concat ]; "list-concat", [ test_case "List mashing" `Slow test_list_concat ]; ] ``` The result is a self-contained binary which displays the test results. Use `dune exec examples/simple.exe -- --help` to see the runtime options. Here's an example of a of failing test suite:

By default, only the first failing test log is printed to the console (and all test logs are captured on disk). Pass `--show-errors` to print all error messages. ### Using Alcotest with opam and Dune Add `(alcotest :with-test)` to the `depends` stanza of your `dune-project` file, or `"alcotest" {with-test}` to your opam file. Use the `with-test` [package variable][with-test] to declare your tests opam dependencies. Call opam to install them: ```sh-session $ opam install --deps-only --with-test . ``` You can then [declare your test][tests] and link with Alcotest: `(test (libraries alcotest …) …)`, and run your tests: ```sh-session $ dune runtest ``` [with-test]: https://opam.ocaml.org/doc/Manual.html#pkgvar-with-test [tests]: https://dune.readthedocs.io/en/stable/tests.html#custom-tests ### Selecting tests to execute You can filter which tests to run by supplying a regular expression matching the names of the tests to execute, or by passing a regular expression _and_ a comma-separated list of test numbers (or ranges of test numbers, e.g. `2,4..9`): ```sh-session $ ./simple.native test '.*concat*' Testing Utils. [SKIP] string-case 0 Lower case. [SKIP] string-case 1 Capitalization. [OK] string-concat 0 String mashing. [OK] list-concat 0 List mashing. The full test results are available in `_build/_tests`. Test Successful in 0.000s. 2 tests run. $ ./simple.native test 'string-case' '1..3' Testing Utils. [SKIP] string-case 0 Lower case. [OK] string-case 1 Capitalization. [SKIP] string-concat 0 String mashing. [SKIP] list-concat 0 List mashing. The full test results are available in `_build/_tests`. Test Successful in 0.000s. 1 test run. ``` Note that you cannot filter by test case name (i.e. `Lower case` or `Capitalization`), you must filter by test name & number instead. See the [examples][] directory for more examples. [examples]: https://github.com/mirage/alcotest/tree/main/examples ### Quick and Slow tests In general you should use `` `Quick`` tests: tests that are ran on any invocations of the test suite. You should only use `` `Slow`` tests for stress tests that are ran only on occasion (typically before a release or after a major change). These slow tests can be suppressed by passing the `-q` flag on the command line, e.g.: ```sh-session $ ./test.exe -q # run only the quick tests $ ./test.exe # run quick and slow tests ``` ### Passing custom options to the tests In most cases, the base tests are `unit -> unit` functions. However, it is also possible to pass an extra option to all the test functions by using `'a -> unit`, where `'a` is the type of the extra parameter. In order to do this, you need to specify how this extra parameter is read on the command-line, by providing a [Cmdliner term][] for command-line arguments which explains how to parse and serialize values of type `'a` (*note:* do not use positional arguments, only optional arguments are supported). [Cmdliner term]: https://erratique.ch/software/cmdliner/doc/Cmdliner/Term/index.html For instance: ```ocaml let test_nice i = Alcotest.(check int) "Is it a nice integer?" i 42 let int = let doc = "What is your preferred number?" in Cmdliner.Arg.(required & opt (some int) None & info ["n"] ~doc ~docv:"NUM") let () = Alcotest.run_with_args "foo" int [ "all", ["nice", `Quick, test_nice] ] ``` Will generate `test.exe` such that: ```sh-session $ test.exe test test.exe: required option -n is missing $ test.exe test -n 42 Testing foo. [OK] all 0 int. ``` ### Lwt Alcotest provides an `Alcotest_lwt` module that you could use to wrap Lwt test cases. The basic idea is that instead of providing a test function in the form `unit -> unit`, you provide one with the type `unit -> unit Lwt.t` and alcotest-lwt calls `Lwt_main.run` for you. However, there are a couple of extra features: - If an async exception occurs, it will cancel your test case for you and fail it (rather than exiting the process). - You get given a switch, which will be turned off when the test case finishes (or fails). You can use that to free up any resources. For instance: ```ocaml let free () = print_endline "freeing all resources"; Lwt.return () let test_lwt switch () = Lwt_switch.add_hook (Some switch) free; Lwt.async (fun () -> failwith "All is broken"); Lwt_unix.sleep 10. let () = Lwt_main.run @@ Alcotest_lwt.run "foo" [ "all", [ Alcotest_lwt.test_case "one" `Quick test_lwt ] ] ``` Will generate: ```sh-session $ test.exe Testing foo. [ERROR] all 0 one. -- all.000 [one.] Failed -- in _build/_tests/all.000.output: freeing all resources [failure] All is broken ``` ### Comparison with other testing frameworks The README is pretty clear about that: > Alcotest is a lightweight and colourful test framework. Alcotest is the only testing framework using colors! More seriously, Alcotest is similar to [ounit][] but it fixes a few of the problems found in that library: - Alcotest has a nicer output, it is easier to see what failed and what succeeded and to read the log outputs of the failed tests; - Alcotest uses combinators to define pretty-printers and comparators between the things to test. Other nice tools doing different kind of testing also exist: - [qcheck][] does random generation and property testing (e.g. Quick Check); - [crowbar][] and [bun][] are similar to qcheck, but use compiler-directed randomness, i.e. they take advantage of the AFL support the OCaml compiler; - [ppx_inline_tests][] allows to write tests in the same file as your source-code; they will be run only in a special mode of compilation. [ounit]: https://github.com/gildor478/ounit [qcheck]: https://github.com/c-cube/qcheck [crowbar]: https://github.com/stedolan/crowbar [bun]: https://github.com/yomimono/ocaml-bun [ppx_inline_tests]: https://github.com/janestreet/ppx_inline_test alcotest-1.7.0/alcotest-async.opam000066400000000000000000000016511437617477400171730ustar00rootroot00000000000000# This file is generated by dune, edit dune-project instead opam-version: "2.0" synopsis: "Async-based helpers for Alcotest" description: "Async-based helpers for Alcotest" maintainer: ["thomas@gazagnaire.org"] authors: ["Thomas Gazagnaire"] license: "ISC" homepage: "https://github.com/mirage/alcotest" doc: "https://mirage.github.io/alcotest" bug-reports: "https://github.com/mirage/alcotest/issues" depends: [ "dune" {>= "3.0"} "re" {with-test} "fmt" {with-test} "cmdliner" {with-test & >= "1.1.0"} "core" {>= "v0.15.0"} "core_unix" {>= "v0.15.0"} "base" "async_kernel" "ocaml" {>= "4.11.0"} "alcotest" {= version} "async" {>= "v0.15.0"} "async_unix" {>= "v0.15.0"} "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] dev-repo: "git+https://github.com/mirage/alcotest.git" alcotest-1.7.0/alcotest-help.txt000066400000000000000000000057301437617477400166730ustar00rootroot00000000000000NAME test.exe - Run all the tests. SYNOPSIS test.exe [COMMAND] … COMMANDS list [--color=WHEN] [OPTION]… List all available tests. test [OPTION]… [NAME_REGEX] [TESTCASES] Run a subset of the tests. ARGUMENTS NAME_REGEX A regular expression matching the names of tests to run TESTCASES A comma-separated list of test case numbers (and ranges of numbers) to run, e.g: '4,6-10,19'. When specifying ranges, both '-' and '..' are accepted as valid separators. OPTIONS --bail (absent ALCOTEST_BAIL env) Stop running tests after the first failure. -c, --compact (absent ALCOTEST_COMPACT env) Compact the output of the tests. --color=WHEN (absent ALCOTEST_COLOR env) Colorize the output. WHEN must be one of auto, always or never. Defaults to 'always' when running inside Dune, otherwise defaults to 'auto'. -e, --show-errors (absent ALCOTEST_SHOW_ERRORS env) Display the test errors. --json Display JSON for the results, to be used by a script. -o DIR Where to store the log files of the tests. -q, --quick-tests (absent ALCOTEST_QUICK_TESTS env) Run only the quick tests. --tail-errors=N (absent ALCOTEST_TAIL_ERRORS env) Show only the last N lines of output in case of an error. -v, --verbose (absent ALCOTEST_VERBOSE env) Display the test outputs. WARNING: when using this option the output logs will not be available for further inspection. COMMON OPTIONS --help[=FMT] (default=auto) Show this help in format FMT. The value FMT must be one of auto, pager, groff or plain. With auto, the format is pager or plain whenever the TERM env var is dumb or undefined. EXIT STATUS test.exe exits with the following status: 0 on success. 123 on indiscriminate errors reported on standard error. 124 on command line parsing errors. 125 on unexpected internal errors (bugs). ENVIRONMENT These environment variables affect the execution of test.exe: ALCOTEST_BAIL See option --bail. ALCOTEST_COLOR See option --color. ALCOTEST_COMPACT See option --compact. ALCOTEST_QUICK_TESTS See option --quick-tests. ALCOTEST_SHOW_ERRORS See option --show-errors. ALCOTEST_TAIL_ERRORS See option --tail-errors. ALCOTEST_VERBOSE See option --verbose. CI Whether Alcotest is running in a CI system, if set to 'true'. GITHUB_ACTIONS Whether Alcotest is running in GitHub Actions, if set to 'true'. Display tests errors and outputs GitHub Actions annotations. OCAMLCI Whether Alcotest is running in OCaml-CI, if set to 'true'. Display tests errors. alcotest-1.7.0/alcotest-js.opam000066400000000000000000000016361437617477400164750ustar00rootroot00000000000000# This file is generated by dune, edit dune-project instead opam-version: "2.0" synopsis: "Virtual package containing optional JavaScript dependencies for Alcotest" description: "Virtual package containing optional JavaScript dependencies for Alcotest" maintainer: ["thomas@gazagnaire.org"] authors: ["Thomas Gazagnaire"] license: "ISC" homepage: "https://github.com/mirage/alcotest" doc: "https://mirage.github.io/alcotest" bug-reports: "https://github.com/mirage/alcotest/issues" depends: [ "dune" {>= "3.0"} "alcotest" {= version} "js_of_ocaml-compiler" {>= "3.11.0"} "fmt" {with-test & >= "0.8.7"} "cmdliner" {with-test & >= "1.1.0"} "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@runtest-js" {with-test} "@doc" {with-doc} ] ] dev-repo: "git+https://github.com/mirage/alcotest.git" alcotest-1.7.0/alcotest-lwt.opam000066400000000000000000000014441437617477400166640ustar00rootroot00000000000000# This file is generated by dune, edit dune-project instead opam-version: "2.0" synopsis: "Lwt-based helpers for Alcotest" description: "Lwt-based helpers for Alcotest" maintainer: ["thomas@gazagnaire.org"] authors: ["Thomas Gazagnaire"] license: "ISC" homepage: "https://github.com/mirage/alcotest" doc: "https://mirage.github.io/alcotest" bug-reports: "https://github.com/mirage/alcotest/issues" depends: [ "dune" {>= "3.0"} "re" {with-test} "cmdliner" {with-test & >= "1.1.0"} "fmt" "ocaml" {>= "4.05.0"} "alcotest" {= version} "lwt" "logs" "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] dev-repo: "git+https://github.com/mirage/alcotest.git" alcotest-1.7.0/alcotest-mirage.opam000066400000000000000000000015271437617477400173240ustar00rootroot00000000000000# This file is generated by dune, edit dune-project instead opam-version: "2.0" synopsis: "Mirage implementation for Alcotest" description: "Mirage implementation for Alcotest" maintainer: ["thomas@gazagnaire.org"] authors: ["Thomas Gazagnaire"] license: "ISC" homepage: "https://github.com/mirage/alcotest" doc: "https://mirage.github.io/alcotest" bug-reports: "https://github.com/mirage/alcotest/issues" depends: [ "dune" {>= "3.0"} "re" {with-test} "cmdliner" {with-test & >= "1.1.0"} "fmt" "ocaml" {>= "4.05.0"} "alcotest" {= version} "mirage-clock" {>= "2.0.0"} "duration" "lwt" "logs" "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] dev-repo: "git+https://github.com/mirage/alcotest.git" alcotest-1.7.0/alcotest.opam000066400000000000000000000024351437617477400160610ustar00rootroot00000000000000# This file is generated by dune, edit dune-project instead opam-version: "2.0" synopsis: "Alcotest is a lightweight and colourful test framework" description: """ Alcotest exposes simple interface to perform unit tests. It exposes a simple TESTABLE module type, a check function to assert test predicates and a run function to perform a list of unit -> unit test callbacks. Alcotest provides a quiet and colorful output where only faulty runs are fully displayed at the end of the run (with the full logs ready to inspect), with a simple (yet expressive) query language to select the tests to run. """ maintainer: ["thomas@gazagnaire.org"] authors: ["Thomas Gazagnaire"] license: "ISC" homepage: "https://github.com/mirage/alcotest" doc: "https://mirage.github.io/alcotest" bug-reports: "https://github.com/mirage/alcotest/issues" depends: [ "dune" {>= "3.0"} "ocaml" {>= "4.05.0"} "fmt" {>= "0.8.7"} "astring" "cmdliner" {>= "1.1.0"} "re" {>= "1.7.2"} "stdlib-shims" "uutf" {>= "1.0.1"} "ocaml-syntax-shims" "odoc" {with-doc} ] conflicts: [ "result" {< "1.5"} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] dev-repo: "git+https://github.com/mirage/alcotest.git" alcotest-1.7.0/dune000066400000000000000000000011541437617477400142400ustar00rootroot00000000000000(rule (with-stdout-to alcotest-help.txt.actual (run examples/simple.exe --help=plain))) (rule (target alcotest-help.txt.processed) (deps (:strip-randomness test/e2e/strip_randomness.exe)) (action (with-outputs-to %{target} (run %{strip-randomness} %{dep:alcotest-help.txt.actual})))) (rule ;; Ideally, this would be attached to [runtest], but currently the manpage ;; fails to render properly on Windows due to a Cmdliner bug: any '\\' ;; characters in default values will be interpreted as (broken) escapes. (alias generatehelp) (action (diff alcotest-help.txt alcotest-help.txt.processed))) alcotest-1.7.0/dune-project000066400000000000000000000050721437617477400157070ustar00rootroot00000000000000(lang dune 3.0) (implicit_transitive_deps false) (generate_opam_files true) (name alcotest) (source (github mirage/alcotest)) (license ISC) (authors "Thomas Gazagnaire") (maintainers "thomas@gazagnaire.org") (package (name alcotest) (synopsis "Alcotest is a lightweight and colourful test framework") (documentation "https://mirage.github.io/alcotest") (description "\ Alcotest exposes simple interface to perform unit tests. It exposes a simple TESTABLE module type, a check function to assert test predicates and a run function to perform a list of unit -> unit test callbacks. Alcotest provides a quiet and colorful output where only faulty runs are fully displayed at the end of the run (with the full logs ready to inspect), with a simple (yet expressive) query language to select the tests to run. ") (depends (ocaml (>= 4.05.0)) (fmt (>= 0.8.7)) astring (cmdliner (>= 1.1.0)) (re (>= 1.7.2)) stdlib-shims (uutf (>= 1.0.1)) ocaml-syntax-shims) (conflicts (result (< 1.5)) ;; Ensure `Result.result` = `Stdlib.result` when possible )) (package (name alcotest-async) (synopsis "Async-based helpers for Alcotest") (description "Async-based helpers for Alcotest") (documentation "https://mirage.github.io/alcotest") (depends (re :with-test) (fmt :with-test) (cmdliner (and :with-test (>= 1.1.0))) (core (>= v0.15.0)) (core_unix (>= v0.15.0)) base async_kernel (ocaml (>= 4.11.0)) (alcotest (= :version)) (async (>= v0.15.0)) (async_unix (>= v0.15.0)))) (package (name alcotest-lwt) (synopsis "Lwt-based helpers for Alcotest") (description "Lwt-based helpers for Alcotest") (documentation "https://mirage.github.io/alcotest") (depends (re :with-test) (cmdliner (and :with-test (>= 1.1.0))) fmt (ocaml (>= 4.05.0)) (alcotest (= :version)) lwt logs)) (package (name alcotest-mirage) (synopsis "Mirage implementation for Alcotest") (description "Mirage implementation for Alcotest") (documentation "https://mirage.github.io/alcotest") (depends (re :with-test) (cmdliner (and :with-test (>= 1.1.0))) fmt (ocaml (>= 4.05.0)) (alcotest (= :version)) (mirage-clock (>= 2.0.0)) duration lwt logs)) (package (name alcotest-js) (synopsis "Virtual package containing optional JavaScript dependencies for Alcotest") (description "Virtual package containing optional JavaScript dependencies for Alcotest") (documentation "https://mirage.github.io/alcotest") (allow_empty) (depends (alcotest (= :version)) (js_of_ocaml-compiler (>= 3.11.0)) (fmt (and :with-test (>= 0.8.7))) (cmdliner (and :with-test (>= 1.1.0))))) alcotest-1.7.0/examples/000077500000000000000000000000001437617477400151775ustar00rootroot00000000000000alcotest-1.7.0/examples/bad/000077500000000000000000000000001437617477400157255ustar00rootroot00000000000000alcotest-1.7.0/examples/bad/bad.ml000066400000000000000000000045021437617477400170060ustar00rootroot00000000000000(* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. For more information, please refer to *) (* Run with [dune exec ./examples/bad/bad.exe] *) (* A module with functions to test *) module To_test = struct let capitalise = Astring.String.Ascii.uppercase let double_all = List.map (fun a -> a + a) end let test_capitalise () = To_test.capitalise "b" |> Alcotest.(check string) "strings" "A" let test_double_all () = To_test.double_all [ 1; 1; 2; 3 ] |> Alcotest.(check (list int)) "int lists" [ 1 ] let suite1 = [ ( "to_test", [ ("capitalise", `Quick, test_capitalise); ("double all", `Slow, test_double_all); ] ); ] let suite2 = [ ( "Ωèone", [ ("Passing test 1", `Quick, fun () -> ()); ( "Failing test", `Quick, fun () -> Alcotest.fail "This was never going to work..." ); ("Passing test 2", `Quick, fun () -> ()); ] ); ] (* Run both suites completely, even if the first contains failures *) let () = try Alcotest.run ~and_exit:false "First suite" suite1 with Alcotest.Test_error -> Printf.printf "Forging ahead regardless!\n%!"; Alcotest.run ~and_exit:false "Second suite" suite2; Printf.printf "Finally done." alcotest-1.7.0/examples/bad/dune000066400000000000000000000003751437617477400166100ustar00rootroot00000000000000(executable (name bad) (modes exe js) (libraries alcotest astring)) ; This fails at runtime, so just build the executable (alias (name runtest) (package alcotest) (deps bad.exe)) (alias (name runtest-js) (package alcotest) (deps bad.bc.js)) alcotest-1.7.0/examples/dune000066400000000000000000000006511437617477400160570ustar00rootroot00000000000000(executables (names simple floats) (modes exe js) (libraries alcotest)) (rule (alias runtest) (package alcotest) (action (run %{dep:floats.exe}))) (rule (alias runtest) (package alcotest) (action (run %{dep:simple.exe}))) (rule (alias runtest-js) (package alcotest-js) (action (run node %{dep:floats.bc.js}))) (rule (alias runtest-js) (package alcotest-js) (action (run node %{dep:simple.bc.js}))) alcotest-1.7.0/examples/floats.ml000066400000000000000000000044151437617477400170250ustar00rootroot00000000000000(* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. For more information, please refer to *) let e = epsilon_float let nan () = Alcotest.(check @@ float e) "NaN is NaN" nan nan; Alcotest.(check @@ neg @@ float e) "NaN is not number" nan 7.; Alcotest.(check @@ neg @@ float e) "number is not NaN" 8. nan let infinity () = Alcotest.(check @@ float e) "+∞ is +∞" infinity infinity; Alcotest.(check @@ float e) "-∞ is -∞" neg_infinity neg_infinity; Alcotest.(check @@ neg @@ float e) "+∞ is not -∞" infinity neg_infinity; Alcotest.(check @@ neg @@ float e) "-∞ is not +∞" neg_infinity infinity; Alcotest.(check @@ neg @@ float e) "+∞ is not 3" infinity 3. let others () = Alcotest.(check @@ float e) "0 is 0" 0. 0.; Alcotest.(check @@ float e) "0 is epsilon" 0. e; Alcotest.(check @@ neg @@ float e) "0 is not 1" 0. 1.; Alcotest.(check @@ neg @@ float e) "1 is not 0" 1. 0.; Alcotest.(check @@ float e) ".3 is .3" (0.1 +. 0.2) 0.3 let edge_set = [ ("NaN", `Quick, nan); ("∞", `Quick, infinity) ] let others_set = [ ("others", `Quick, others) ] let () = Alcotest.run "Float tests" [ ("Edge cases", edge_set); ("Other floats", others_set) ] alcotest-1.7.0/examples/lwt/000077500000000000000000000000001437617477400160055ustar00rootroot00000000000000alcotest-1.7.0/examples/lwt/dune000066400000000000000000000001361437617477400166630ustar00rootroot00000000000000(tests (names test) (package alcotest-lwt) (libraries alcotest alcotest-lwt lwt lwt.unix)) alcotest-1.7.0/examples/lwt/test.ml000066400000000000000000000067211437617477400173240ustar00rootroot00000000000000(* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. For more information, please refer to *) open Lwt.Infix exception Library_exception module To_test = struct let lowercase = String.lowercase_ascii let lowercase_lwt s = Lwt.return (lowercase s) let exn () = raise Library_exception let exn_lwt_toplevel () : unit Lwt.t = raise Library_exception let exn_lwt_internal () : unit Lwt.t = Lwt.return (raise Library_exception) end (* The tests *) let test_lowercase () = Alcotest.(check string) "same string" "hello!" (To_test.lowercase "hELLO!") let test_lowercase_lwt _ () = To_test.lowercase_lwt "hELLO!" >|= Alcotest.(check string) "same string" "hello!" let test_exn () = Alcotest.check_raises "custom exception" Library_exception To_test.exn let lwt_check_raises f = Lwt.catch (fun () -> f () >|= fun () -> `Ok) (function e -> Lwt.return @@ `Error e) >|= function | `Ok -> Alcotest.fail "No exception was thrown" | `Error Library_exception -> Alcotest.(check pass) "Correct exception" () () | `Error _ -> Alcotest.fail "Incorrect exception was thrown" let test_exn_lwt_toplevel _ () = lwt_check_raises To_test.exn_lwt_toplevel let test_exn_lwt_internal _ () = lwt_check_raises To_test.exn_lwt_internal let switch = ref None let test_switch_alloc s () = Lwt.return_unit >|= fun () -> switch := Some s; Alcotest.(check bool) "Passed switch is initially on" (Lwt_switch.is_on s) true let test_switch_dealloc _ () = Lwt.return_unit >|= fun () -> match !switch with | None -> Alcotest.fail "No switch left by `test_switch_alloc` test" | Some s -> Alcotest.(check bool) "Switch is disabled after test" (Lwt_switch.is_on s) false (* Run it *) let () = let open Alcotest_lwt in Lwt_main.run @@ run "LwtUtils" [ ( "basic", [ test_case_sync "Plain" `Quick test_lowercase; test_case "Lwt" `Quick test_lowercase_lwt; ] ); ( "exceptions", [ test_case_sync "Plain" `Quick test_exn; test_case "Lwt toplevel" `Quick test_exn_lwt_toplevel; test_case "Lwt internal" `Quick test_exn_lwt_internal; ] ); ( "switches", [ test_case "Allocate resource" `Quick test_switch_alloc; test_case "Check resource deallocated" `Quick test_switch_dealloc; ] ); ] alcotest-1.7.0/examples/simple.ml000066400000000000000000000044451437617477400170310ustar00rootroot00000000000000(* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. For more information, please refer to *) (* A module with functions to test *) module To_test = struct let lowercase = String.lowercase_ascii let capitalize = String.capitalize_ascii let str_concat = String.concat "" let list_concat = List.append end (* The tests *) let test_lowercase () = Alcotest.(check string) "same string" "hello!" (To_test.lowercase "hELLO!") let test_capitalize () = Alcotest.(check string) "same string" "World." (To_test.capitalize "world.") let test_str_concat () = Alcotest.(check string) "same string" "foobar" (To_test.str_concat [ "foo"; "bar" ]) let test_list_concat () = Alcotest.(check (list int)) "same lists" [ 1; 2; 3 ] (To_test.list_concat [ 1 ] [ 2; 3 ]) (* Run it *) let () = Alcotest.run "Utils" [ ( "string-case", [ Alcotest.test_case "Lower case" `Quick test_lowercase; Alcotest.test_case "Capitalization" `Quick test_capitalize; ] ); ( "string-concat", [ Alcotest.test_case "String mashing" `Quick test_str_concat ] ); ( "list-concat", [ Alcotest.test_case "List mashing" `Slow test_list_concat ] ); ] alcotest-1.7.0/src/000077500000000000000000000000001437617477400141505ustar00rootroot00000000000000alcotest-1.7.0/src/alcotest-async/000077500000000000000000000000001437617477400171015ustar00rootroot00000000000000alcotest-1.7.0/src/alcotest-async/alcotest_async.ml000066400000000000000000000014061437617477400224470ustar00rootroot00000000000000open Async open Async_unix let run_test timeout name fn args = Clock.with_timeout timeout (fn args) >>| function | `Result x -> x | `Timeout -> Alcotest.fail (Printf.sprintf "%s timed out after %s" name (Time_unix.Span.to_string_hum timeout)) module Promise = struct include Deferred let bind x f = bind x ~f let catch t on_error = try_with t >>= function Ok a -> return a | Error exn -> on_error exn end module V1 = struct module Tester = Alcotest_engine.V1.Cli.Make (Alcotest.Unix_platform) (Promise) include Tester let test_case_sync n s f = test_case n s (fun x -> Deferred.return (f x)) let test_case ?(timeout = Time_unix.Span.of_sec 2.) name s f = test_case name s (run_test timeout name f) end include V1 alcotest-1.7.0/src/alcotest-async/alcotest_async.mli000066400000000000000000000017441437617477400226250ustar00rootroot00000000000000(* * Copyright (c) 2017 Rudi Grinberg * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) (** [Alcotest_async] enables testing functions which return an Async deferred. {!run} returns a deferred which will run the tests when scheduled. *) include Alcotest_async_intf.Alcotest_async alcotest-1.7.0/src/alcotest-async/alcotest_async_intf.ml000066400000000000000000000010401437617477400234610ustar00rootroot00000000000000module type V1 = sig include Alcotest_engine.V1.Cli.S with type return = unit Async.Deferred.t val test_case : ?timeout:Time_unix.Span.t -> string -> Alcotest.speed_level -> ('a -> unit Async.Deferred.t) -> 'a test_case val test_case_sync : string -> Alcotest.speed_level -> ('a -> unit) -> 'a test_case end module type Alcotest_async = sig include V1 (** {1 Versioned APIs} *) module V1 : V1 (** An alias of the above API that provides a stability guarantees over major version changes. *) end alcotest-1.7.0/src/alcotest-async/dune000066400000000000000000000002611437617477400177560ustar00rootroot00000000000000(library (name alcotest_async) (public_name alcotest-async) (libraries alcotest.engine alcotest async async_kernel async_unix base core core_unix.time_unix)) alcotest-1.7.0/src/alcotest-engine/000077500000000000000000000000001437617477400172315ustar00rootroot00000000000000alcotest-1.7.0/src/alcotest-engine/alcotest_engine.ml000066400000000000000000000002711437617477400227260ustar00rootroot00000000000000module V1 = struct module Test = Test module Core = Core.V1 module Cli = Cli.V1 end module Monad = Monad module Platform = Platform module Private = struct module Pp = Pp end alcotest-1.7.0/src/alcotest-engine/alcotest_engine.mli000066400000000000000000000042141437617477400231000ustar00rootroot00000000000000(* * Copyright (c) 2013-2016 Thomas Gazagnaire * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) (** [Alcotest_engine] provides a platform-independent test framework. The main building blocks and combinators are defined here. These can be used to defined tests. The platform-specific runners for these tests are in [alcotest], [alcotest-lwt], [alcotest-async] and [alcotest-mirage]. *) module V1 : sig (** Version 1 of the user-facing Alcotest API. *) (** {1 Assert functions} *) module Test = Test (** {1 Monadic test runners} *) (** These modules provide the ability to run tests inside a concurrency monad: that is, to sequence test cases of type ['a -> unit m] into a computation of type ['a -> unit m] (for some concurrency monad [m]) with can then be scheduled in a main event loop. For tests using [Lwt.t] or [Async_kernel.Deferred.t], use the [Alcotest_lwt] and [Alcotest_async] packages directly. *) module Core = Core.V1 (** Defines monadic test runners {i without} command-line interfaces. *) module Cli = Cli.V1 (** Wraps {!Core} to provide a command-line interface. *) end module Monad = Monad (** Monad signatures for use with {!V1.Core} and {!V1.Cli}. *) module Platform = Platform (** Defines platform-dependent functions. *) (** These modules are exposed for use internally by other Alcotest packages. They do not provide a stable interface. *) module Private : sig module Pp = Pp end alcotest-1.7.0/src/alcotest-engine/callsite_loc.412.ml000066400000000000000000000051441437617477400225310ustar00rootroot00000000000000(* * Copyright (c) 2022 Antonin Décimo * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) (** Best-effort reporting of the location of the call to Alcotest.check. *) let check_caller caller entry = match Printexc.backtrace_slots_of_raw_entry entry with | Some [| slot |] -> ( match Printexc.Slot.name slot with | Some name when caller = name -> true | _ -> false) | _ -> false let get ?(__FUNCTION__ = "Alcotest_engine__Test.check") () = let caller = __FUNCTION__ in let open Printexc in let callstack_depth = 3 (* this function, caller, bound of test *) in let raw_backtrace = get_callstack callstack_depth in let entries = raw_backtrace_entries raw_backtrace in if Array.length entries >= callstack_depth && check_caller caller (Array.unsafe_get entries 1) then match backtrace_slots_of_raw_entry (Array.unsafe_get entries 2) with | Some [| slot |] -> ( match Slot.name slot with | Some bound when Alcotest_stdlib_ext.String.( is_prefix ~affix:"Alcotest_engine__Core." bound || is_prefix ~affix:"Alcotest_lwt." bound || is_prefix ~affix:"Alcotest_async." bound || is_prefix ~affix:"Alcotest_mirage." bound) -> None | Some _ -> Option.map (fun { filename; line_number; start_char; end_char = _ } -> { Lexing.pos_fname = filename; pos_lnum = line_number; pos_bol = 0; pos_cnum = start_char; }) (Slot.location slot) | None -> None) | _ -> None else None let get ?__FUNCTION__ () = let guess = match Sys.getenv "ALCOTEST_SOURCE_CODE_POSITION" with | "" | "false" | "no" | "n" | "0" -> false | "true" | "yes" | "y" | "1" -> true | _ | (exception _) -> true in if guess then get ?__FUNCTION__ () else None alcotest-1.7.0/src/alcotest-engine/cli.ml000066400000000000000000000140741437617477400203400ustar00rootroot00000000000000(* * Copyright (c) 2013-2016 Thomas Gazagnaire * Copyright (c) 2019 Craig Ferguson * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) include Cli_intf open! Import open Cmdliner module Make (P : Platform.MAKER) (M : Monad.S) : V1_types.S with type return = unit M.t = struct (** *) (** The priority order for determining options should be as follows: + 1. if a CLI flag/option is _explicitly_ set, use that; + 2. if the corresponding environment variable is _explicitly_ set, use that; + 3. if the flag/option is set by [run ?argv] + 4. if the flag/option is passed to [run] directly, use that; + 5. otherwise, use the default behaviour set by {!Alcotest.Core}. *) module C = Core.V1.Make (P) (M) include C module P = P (M) open Cmdliner_syntax let ci_env = let doc = Printf.sprintf "Whether Alcotest is running in a CI system, if set to %s." (Arg.doc_quote "true") in Cmdliner.Cmd.Env.info "CI" ~doc let github_action_env = let doc = Printf.sprintf "Whether Alcotest is running in GitHub Actions, if set to %s. Display \ tests errors and outputs GitHub Actions annotations." (Arg.doc_quote "true") in Cmdliner.Cmd.Env.info "GITHUB_ACTIONS" ~doc let ocamlci_env = let doc = Printf.sprintf "Whether Alcotest is running in OCaml-CI, if set to %s. Display tests \ errors." (Arg.doc_quote "true") in Cmdliner.Cmd.Env.info "OCAMLCI" ~doc let alcotest_source_code_position = let doc = "Whether Alcotest should guess the source code position of test \ failures, if any. Defaults to true, set to a falsy value to disable." in Cmdliner.Cmd.Env.info "ALCOTEST_SOURCE_CODE_POSITION" ~doc let envs = [ ci_env; github_action_env; ocamlci_env; alcotest_source_code_position ] let set_color = let env = Cmd.Env.info "ALCOTEST_COLOR" in let+ color_flag = let enum = [ ("auto", `Auto); ("always", `Ansi_tty); ("never", `None) ] in let color = Arg.enum enum in let enum_alts = Arg.doc_alts_enum enum in let doc = Fmt.str "Colorize the output. $(docv) must be %s. Defaults to %s when \ running inside Dune, otherwise defaults to %s." enum_alts (Arg.doc_quote "always") (Arg.doc_quote "auto") in Arg.( value & opt (some color) None & info [ "color" ] ~env ~doc ~docv:"WHEN") in let style_renderer = match color_flag with | Some `Auto -> None | Some (`Ansi_tty | `None) as a -> a | None -> ( try (* Default to [always] when running inside Dune *) let (_ : string) = Sys.getenv "INSIDE_DUNE" in Some `Ansi_tty with Not_found -> None) in P.setup_std_outputs ?style_renderer () let default_cmd config args library_name tests = let and_exit = Config.User.and_exit config and record_backtrace = Config.User.record_backtrace config and ci = Config.User.ci config in let exec_name = Filename.basename Sys.argv.(0) in let doc = "Run all the tests." in let term = let+ () = set_color and+ cli_config = Config.User.term ~and_exit ~record_backtrace ~ci and+ args = args in let config = Config.User.(cli_config || config) in run_with_args' config library_name args tests in (term, Cmd.info exec_name ~doc ~envs) let test_cmd config args library_name tests = let ci = Config.User.ci config in let doc = "Run a subset of the tests." in let term = let+ () = set_color and+ cli_config = Config.User.term ~and_exit:true ~record_backtrace:true ~ci and+ args = args in let config = Config.User.(cli_config || config) in run_with_args' config library_name args tests in (term, Cmd.info "test" ~doc ~envs) let list_cmd tests = let doc = "List all available tests." in ( (let+ () = set_color in list_tests tests), Cmd.info "list" ~doc ) let run_with_args' (type a) ~argv config name (args : a Term.t) (tl : a test list) = let ( >>= ) = M.bind in let choices = List.map (fun (term, info) -> Cmd.v info term) [ list_cmd tl; test_cmd config args name tl ] in let and_exit = Config.User.and_exit config in let exit_or_return exit_code = if and_exit then exit exit_code else M.return () in let result = let default, info = default_cmd config args name tl in Cmd.eval_value ?argv ~catch:and_exit (* Only log exceptions not raised to the user code *) (Cmd.group ~default info choices) in match result with | Ok (`Ok unit_m) -> unit_m >>= fun () -> exit_or_return Cmd.Exit.ok | Ok (`Help | `Version) -> exit_or_return Cmd.Exit.ok | Error (`Parse | `Term) -> exit_or_return Cmd.Exit.cli_error | Error `Exn -> exit Cmd.Exit.internal_error let run_with_args ?and_exit ?verbose ?compact ?tail_errors ?quick_only ?show_errors ?json ?filter ?log_dir ?bail ?record_backtrace ?ci ?argv = Config.User.kcreate (run_with_args' ~argv) ?and_exit ?verbose ?compact ?tail_errors ?quick_only ?show_errors ?json ?filter ?log_dir ?bail ?record_backtrace ?ci let run = Config.User.kcreate (fun config ?argv name tl -> run_with_args' config ~argv name (Term.const ()) tl) end module V1 = struct include V1_types module Make = Make end alcotest-1.7.0/src/alcotest-engine/cli.mli000066400000000000000000000023521437617477400205050ustar00rootroot00000000000000(* * Copyright (c) 2013-2016 Thomas Gazagnaire * Copyright (c) 2019 Craig Ferguson * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) (** This module extends {!Core} to allow CLI options to be passed to Alcotest executables. In particular: - tests can be selectively executed using the "test" subcommand; - the {!run_with_args} function can be used to pass arguments to tests from the command line; - all of the regular options to Alcotest.run can be set via CLI flags. *) include Cli_intf.Cli (** @inline *) alcotest-1.7.0/src/alcotest-engine/cli_intf.ml000066400000000000000000000053341437617477400213570ustar00rootroot00000000000000(* * Copyright (c) 2013-2016 Thomas Gazagnaire * Copyright (c) 2019 Craig Ferguson * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) open! Import module V1_types = struct module type S = sig include Core.V1.S (** @inline *) val run : (?argv:string array -> string -> unit test list -> return) with_options (** [run n t] runs the test suite [t]. [n] is the name of the tested library. The optional argument [and_exit] controls what happens when the function ends. By default, [and_exit] is set, which makes the function exit with [0] if everything is fine or [1] if there is an issue. If [and_exit] is [false], then the function raises [Test_error] on error. The optional argument [argv] specifies command line arguments sent to alcotest like ["--json"], ["--verbose"], etc. Note that this array will be treated like a regular [Sys.argv], so the array must have at least one element, and the first element will be treated as if it was the command name and thus ignored for the purposes of option processing. So [~argv:\[||\]] is an error, [~argv:\[| "--verbose" |\]] will have no effect, and [~argv:\[| "ignored"; "--verbose" |\]] will successfully pass the verbose option. *) val run_with_args : (?argv:string array -> string -> 'a Cmdliner.Term.t -> 'a test list -> return) with_options (** [run_with_args n a t] Similar to [run a t] but take an extra argument [a]. Every test function will receive as argument the evaluation of the [Cmdliner] term [a]: this is useful to configure the test behaviors using the CLI. *) end module type MAKER = functor (_ : Platform.MAKER) (M : Monad.S) -> S with type return = unit M.t end module type Cli = sig module V1 : sig module type S = V1_types.S module type MAKER = V1_types.MAKER module Make (_ : Platform.MAKER) (M : Monad.S) : V1_types.S with type return = unit M.t end end alcotest-1.7.0/src/alcotest-engine/config.ml000066400000000000000000000250221437617477400210310ustar00rootroot00000000000000include Config_intf include Config_intf.Types open! Import open Cmdliner_syntax (* Keys are configuration properties, which have defaults and may be customisable via a CLI. *) module Key = struct module type S = sig type t val default : t end module type Cli = sig include S val term : t option Cmdliner.Term.t end module Flag (X : sig val term : bool Cmdliner.Term.t end) : Cli with type t = bool = struct type t = bool let default = false (* If a Cmdliner flag is _not_ set, we interpret it as 'use the program default' rather than an explicit 'disable'. This changes the type of {!Cmdliner.Arg.flag} to reflect that fact. *) let term = X.term >>| function true -> Some true | false -> None end (** {1 Definitions of supported keys} *) module Arg = Cmdliner.Arg module Cmd = Cmdliner.Cmd module And_exit = struct type t = bool let default = true end module Record_backtrace = struct type t = bool let default = true end module CI = struct type t = ci let default = let ci = match Sys.getenv "CI" with | "true" -> true | _ | (exception Not_found) -> false and github_actions = match Sys.getenv "GITHUB_ACTIONS" with | "true" -> true | _ | (exception Not_found) -> false and ocamlci = match Sys.getenv "OCAMLCI" with | "true" -> true | _ | (exception Not_found) -> false in match (ci, github_actions, ocamlci) with | true, true, false -> `Github_actions | true, false, true -> `OCamlci | true, false, false -> `Unknown | _ -> `Disabled end module Verbose = Flag (struct let term = let env = Cmd.Env.info "ALCOTEST_VERBOSE" in let doc = "Display the test outputs. $(b,WARNING:) when using this option the \ output logs will not be available for further inspection." in Arg.(value & flag & info ~env [ "v"; "verbose" ] ~docv:"" ~doc) end) module Compact = Flag (struct let term = let env = Cmd.Env.info "ALCOTEST_COMPACT" in let doc = "Compact the output of the tests." in Arg.(value & flag & info ~env [ "c"; "compact" ] ~docv:"" ~doc) end) module Bail = Flag (struct let term = let env = Cmd.Env.info "ALCOTEST_BAIL" in let doc = "Stop running tests after the first failure." in Arg.(value & flag & info ~env [ "bail" ] ~docv:"" ~doc) end) module Json = Flag (struct let term = let doc = "Display JSON for the results, to be used by a script." in Arg.(value & flag & info [ "json" ] ~docv:"" ~doc) end) module Show_errors = Flag (struct let term = let env = Cmd.Env.info "ALCOTEST_SHOW_ERRORS" in let doc = "Display the test errors." in Arg.(value & flag & info ~env [ "e"; "show-errors" ] ~docv:"" ~doc) end) module Quick_only = Flag (struct let term = let env = Cmd.Env.info "ALCOTEST_QUICK_TESTS" in let doc = "Run only the quick tests." in Arg.(value & flag & info ~env [ "q"; "quick-tests" ] ~docv:"" ~doc) end) module Tail_errors = struct type t = bound let default = `Unlimited let limit_parser s = match s with | "unlimited" -> Ok `Unlimited | s -> ( try let n = int_of_string s in if n < 0 then Error (`Msg "numeric limit must be nonnegative or 'unlimited'") else Ok (`Limit n) with Failure _ -> Error (`Msg "invalid numeric limit")) let limit_printer ppf limit = match limit with | `Unlimited -> Fmt.pf ppf "unlimited" | `Limit n -> Fmt.pf ppf "%i" n (* Parse/print a nonnegative number of lines or "unlimited". *) let limit = Arg.conv (limit_parser, limit_printer) let term = let env = Cmd.Env.info "ALCOTEST_TAIL_ERRORS" in let doc = "Show only the last $(docv) lines of output in case of an error." in Arg.( value & opt (some limit) None & info ~env [ "tail-errors" ] ~docv:"N" ~doc) end module Log_dir = struct type t = string option let term = let doc = "Where to store the log files of the tests." in Arg.(value & opt (some dir) None & info [ "o" ] ~docv:"DIR" ~doc) end module Filter = struct type t = filter let regex : Re.re Arg.conv = let parse s = try Ok Re.(compile @@ Pcre.re s) with | Re.Perl.Parse_error -> Error (`Msg "Perl-compatible regexp parse error") | Re.Perl.Not_supported -> Error (`Msg "unsupported regexp feature") in let print = Re.pp_re in Arg.conv (parse, print) let int_range_list : int list Arg.conv = let exception Invalid_format in let parse s = let rec range lower upper acc = if lower > upper then acc else range (succ lower) upper (lower :: acc) in let process_range acc s = String.cuts ~sep:".." s |> List.concat_map (String.cuts ~sep:"-") |> List.map String.to_int |> function | [ Some i ] -> i :: acc | [ Some lower; Some upper ] when lower <= upper -> range lower upper acc | _ -> raise Invalid_format in let ranges = String.cuts ~sep:"," s in match List.fold_left process_range [] ranges with | list -> Ok list | exception Invalid_format -> Error (`Msg "must be a comma-separated list of integers / integer ranges") in let print ppf set = Fmt.(braces @@ list ~sep:comma int) ppf set in Arg.conv (parse, print) let term = let+ name_regex = let doc = "A regular expression matching the names of tests to run" in Arg.(value & pos 0 (some regex) None & info [] ~doc ~docv:"NAME_REGEX") and+ index_cases = let doc = "A comma-separated list of test case numbers (and ranges of numbers) \ to run, e.g: '4,6-10,19'. When specifying ranges, both '-' and '..' \ are accepted as valid separators." in Arg.( value & pos 1 (some int_range_list) None & info [] ~doc ~docv:"TESTCASES") in match (name_regex, index_cases) with | None, None -> None | _, _ -> let name_filter = match name_regex with | None -> fun _ -> true | Some r -> fun n -> Re.execp r n in let index_filter = match index_cases with | None -> fun _ -> true | Some ints -> let set = Int.Set.of_list ints in fun i -> Int.Set.mem i set in Some (fun ~name ~index -> if name_filter name && index_filter index then `Run else `Skip) end end (* User configs before defaults have been applied. *) module User = struct open Key type t = { and_exit : And_exit.t option; verbose : Verbose.t option; compact : Compact.t option; tail_errors : Tail_errors.t option; quick_only : Quick_only.t option; show_errors : Show_errors.t option; json : Json.t option; filter : Filter.t option; (* TODO: set Log_dir default internally *) log_dir : Log_dir.t; bail : Bail.t option; record_backtrace : Record_backtrace.t option; ci : CI.t option; } let ( || ) a b = let merge_on f = Option.(f a || f b) in { and_exit = merge_on (fun t -> t.and_exit); verbose = merge_on (fun t -> t.verbose); compact = merge_on (fun t -> t.compact); tail_errors = merge_on (fun t -> t.tail_errors); quick_only = merge_on (fun t -> t.quick_only); show_errors = merge_on (fun t -> t.show_errors); json = merge_on (fun t -> t.json); filter = merge_on (fun t -> t.filter); log_dir = merge_on (fun t -> t.log_dir); bail = merge_on (fun t -> t.bail); record_backtrace = merge_on (fun t -> t.record_backtrace); ci = merge_on (fun t -> t.ci); } let term ~and_exit ~record_backtrace ~ci = let+ verbose = Verbose.term and+ compact = Compact.term and+ tail_errors = Tail_errors.term and+ show_errors = Show_errors.term and+ quick_only = Quick_only.term and+ json = Json.term and+ filter = Filter.term and+ log_dir = Log_dir.term and+ bail = Bail.term in { and_exit = Some and_exit; verbose; compact; tail_errors; show_errors; quick_only; json; filter; log_dir; bail; record_backtrace = Some record_backtrace; ci = Some ci; } (* Lift a config-sensitive function to one that consumes optional arguments that override config defaults. *) let kcreate : 'a. (t -> 'a) -> 'a with_options = fun f ?and_exit ?verbose ?compact ?tail_errors ?quick_only ?show_errors ?json ?filter ?log_dir ?bail ?record_backtrace ?ci -> f { and_exit; verbose; compact; tail_errors; quick_only; show_errors; json; filter; log_dir; bail; record_backtrace; ci; } let create : (unit -> t) with_options = kcreate (fun t () -> t) let and_exit t = Option.value ~default:And_exit.default t.and_exit let record_backtrace t = Option.value ~default:Record_backtrace.default t.record_backtrace let ci t = Option.value ~default:CI.default t.ci end let apply_defaults ~default_log_dir : User.t -> t = fun { and_exit; verbose; compact; tail_errors; quick_only; show_errors; json; filter; log_dir; bail; record_backtrace; ci; } -> let open Key in object (self) method and_exit = Option.value ~default:And_exit.default and_exit method verbose = Option.value ~default:Verbose.default verbose method compact = Option.value ~default:Compact.default compact method tail_errors = Option.value ~default:Tail_errors.default tail_errors method quick_only = Option.value ~default:Quick_only.default quick_only method show_errors = match (show_errors, self#ci) with | Some show_errors, _ -> show_errors | None, `Disabled -> Show_errors.default | None, _ -> true method json = Option.value ~default:Json.default json method filter = filter method log_dir = Option.value ~default:default_log_dir log_dir method bail = Option.value ~default:Bail.default bail method record_backtrace = Option.value ~default:Record_backtrace.default record_backtrace method ci = Option.value ~default:CI.default ci end alcotest-1.7.0/src/alcotest-engine/config.mli000066400000000000000000000000521437617477400211760ustar00rootroot00000000000000include Config_intf.Config (** @inline *) alcotest-1.7.0/src/alcotest-engine/config_intf.ml000066400000000000000000000033721437617477400220550ustar00rootroot00000000000000module Types = struct type bound = [ `Unlimited | `Limit of int ] type filter = name:string -> index:int -> [ `Run | `Skip ] type ci = [ `Github_actions | `OCamlci | `Unknown | `Disabled ] (** All supported Continuous Integration (CI) systems. *) type t = < and_exit : bool ; verbose : bool ; compact : bool ; tail_errors : bound ; quick_only : bool ; show_errors : bool ; json : bool ; filter : filter option ; log_dir : string ; bail : bool ; record_backtrace : bool ; ci : ci > type 'a with_options = ?and_exit:bool -> ?verbose:bool -> ?compact:bool -> ?tail_errors:bound -> ?quick_only:bool -> ?show_errors:bool -> ?json:bool -> ?filter:filter -> ?log_dir:string -> ?bail:bool -> ?record_backtrace:bool -> ?ci:ci -> 'a end module type Config = sig include module type of Types module User : sig type t (** The type of configurations supplied by the user, with defaults not yet supplied. *) val create : (unit -> t) with_options (** Build a config object with the supplied options. *) val kcreate : (t -> 'a) -> 'a with_options (** Like [create], but passes the constructed config to a continuation rather than returning directly. *) val term : and_exit:bool -> record_backtrace:bool -> ci:ci -> t Cmdliner.Term.t (** [term] provides a command-line interface for building configs. *) val ( || ) : t -> t -> t (** Merge two configs, with fields from the left taking priority over those in the right. *) (** {2 Accessors} *) val and_exit : t -> bool val record_backtrace : t -> bool val ci : t -> ci end val apply_defaults : default_log_dir:string -> User.t -> t end alcotest-1.7.0/src/alcotest-engine/core.ml000066400000000000000000000334021437617477400205150ustar00rootroot00000000000000(* * Copyright (c) 2013-2016 Thomas Gazagnaire * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) include Core_intf open! Import open Model exception Check_error of unit Fmt.t exception Skip let () = let print_error = (* We instantiate the error print buffer lazily, so as to be sensitive to [Fmt_tty.setup_std_outputs]. *) lazy (let buf = Buffer.create 0 in let ppf = Format.formatter_of_buffer buf in Fmt.set_style_renderer ppf Fmt.(style_renderer stderr); fun error -> Fmt.pf ppf "Alcotest assertion failure@.%a@." error (); let contents = Buffer.contents buf in Buffer.clear buf; contents) in Printexc.register_printer (function | Check_error err -> Some (Lazy.force print_error err) | _ -> None) module Make (P : Platform.MAKER) (M : Monad.S) = struct module P = P (M) module Pp = struct include Pp include Pp.Make (P) end module M = Monad.Extend (M) module Suite = Suite (M) module Log_trap = Log_trap.Make (M) (P) include M.Syntax (* Types *) type return = unit M.t type 'a run = 'a -> unit M.t type speed_level = [ `Quick | `Slow ] exception Test_error type 'a test_case = string * speed_level * 'a run let test_case n s f = (n, s, f) type 'a test = string * 'a test_case list (* global state *) type 'a t = { (* library values. *) suite : 'a Suite.t; (* runtime state. *) mutable errors : unit Fmt.t list; (* runtime options. *) max_label : int; (** Longest test label in the suite, in UTF-8 characters. *) config : Config.t; run_id : string; log_trap : Log_trap.t; } let gen_run_id = let random_state = lazy (Random.State.make_self_init ()) in let random_hex _ = let state = Lazy.force random_state in match Random.State.int state 36 with | n when n < 10 -> Char.chr (n + Char.code '0') | n -> Char.chr (n - 10 + Char.code 'A') in fun () -> String.v ~len:8 random_hex let empty ~config ~trap_logs ~suite_name:unescaped_name = let errors = [] in let suite = match Suite.v ~name:unescaped_name with | Ok s -> s | Error `Empty_name -> Pp.user_error "Suite name cannot cannot be empty. Please pass a non-empty string \ to `run`." in let max_label = 0 in let run_id = gen_run_id () in let log_trap = match trap_logs with | false -> Log_trap.inactive | true -> Log_trap.active ~root:config#log_dir ~uuid:run_id ~suite_name:(Suite.name suite) in { suite; errors; max_label; config; run_id; log_trap } let compare_speed_level s1 s2 = match (s1, s2) with | `Quick, `Quick | `Slow, `Slow -> 0 | `Quick, _ -> 1 | _, `Quick -> -1 let pp_suite_results t = let log_dir = Log_trap.pp_current_run_dir t.log_trap in Pp.suite_results ~log_dir t.config let pp_event ~isatty ~prior_error ~tests_so_far t = let config = t.config in let selector_on_failure = (not prior_error) && not (config#verbose || config#show_errors) in if not config#json then Pp.event ~isatty ~compact:config#compact ~max_label:t.max_label ~doc_of_test_name:(Suite.doc_of_test_name t.suite) ~selector_on_failure ~tests_so_far else Fmt.nop let pp_info t = Pp.info ~max_label:t.max_label ~doc_of_test_name:(Suite.doc_of_test_name t.suite) let color c ppf fmt = Fmt.(styled c string) ppf fmt let red_s fmt = color `Red fmt let red ppf fmt = Fmt.kstr (fun str -> red_s ppf str) fmt let pp_error t ppf e = let path, error_fmt = match e with `Error (p, f) -> (p, f) | `Exn (p, _, f) -> (p, f) in let pp_logs ppf () = let pp_logs = Log_trap.recover_logs ~tail:t.config#tail_errors t.log_trap path in match (t.config#verbose, pp_logs) with | true, _ | _, None -> Fmt.pf ppf "%a@," error_fmt () | false, Some pp_logs -> let pp_log_dir = Pp.map_theta ~f:(fun s -> Pp.quoted (Fmt.styled `Cyan s)) (Log_trap.pp_log_location t.log_trap path) in Fmt.pf ppf "%tLogs saved to %t.@," pp_logs pp_log_dir in Fmt.( Pp.with_surrounding_box (const (Pp.event_line ~margins:3 ~max_label:t.max_label ~doc_of_test_name:(Suite.doc_of_test_name t.suite)) (`Result (path, e))) ++ pp_logs ++ Pp.horizontal_rule ++ cut) ppf () let has_run : Run_result.t -> bool = function | `Ok | `Error _ | `Exn _ -> true | `Skip | `Todo _ -> false let bt () = match Printexc.get_backtrace () with "" -> "" | s -> "\n" ^ s let exn path name pp = `Exn (path, name, Fmt.(pp ++ const lines (bt ()))) let protect_test path (f : 'a run) : 'a -> Run_result.t M.t = fun args -> M.catch (fun () -> f args >|= fun () -> `Ok) ((function | Check_error err -> let err = Fmt.(err ++ const string (bt ())) in `Error (path, err) | Skip -> `Skip | Failure s -> exn path "failure" Fmt.(const string s) | Invalid_argument s -> exn path "invalid" Fmt.(const string s) | e -> exn path "exception" Fmt.(const exn e)) >> M.return) type running_state = { tests_so_far : int; first_error : int option } (** State that is kept during the test executions. *) let with_captured_logs t name fn args = if t.config#verbose then fn args else Log_trap.with_captured_logs t.log_trap name (fun () -> (* When capturing the logs of a test, also add the result of the test at the end. *) let+ result = fn args in Pp.rresult_error Fmt.stdout result; result) () let perform_test t args { tests_so_far; first_error } (test : _ Suite.test_case) = let open Suite in let print_event = pp_event t ~prior_error:(Option.is_some first_error) ~tests_so_far ~isatty:(P.stdout_isatty ()) Fmt.stdout in let* () = M.return () in print_event (`Start test.name); let+ result, errored = match test.fn with | `Skip -> M.return (`Skip, false) | `Run fn -> Fmt.(flush stdout) () (* Show event before any test stderr *); let+ result = with_captured_logs t test.name fn args in (* Store errors *) let errored : bool = let error, errored = match result with | (`Error _ | `Exn (_, _, _)) as e -> ([ Fmt.const (pp_error t) e ], true) | _ -> ([], false) in t.errors <- error @ t.errors; errored in (* Show any remaining test output before the event *) Fmt.(flush stdout ()); Fmt.(flush stderr ()); (result, errored) in print_event (`Result (test.name, result)); let error = if errored then Some tests_so_far else None in let state = { tests_so_far = tests_so_far + 1; first_error = Option.(first_error || error); } in (state, result) let perform_tests t tests args = let currently_bailing acc = Option.is_some acc.first_error && t.config#bail in let+ state, test_results = M.List.fold_map_s (fun acc test -> if currently_bailing acc then M.return ({ acc with tests_so_far = succ acc.tests_so_far }, `Skip) else perform_test t args acc test) { tests_so_far = 0; first_error = None } tests in let () = if currently_bailing state then match state.tests_so_far - Option.get_exn state.first_error - 1 with | n when n > 0 -> Fmt.pr "@\n %a@\n" Fmt.(styled `Faint string) (Fmt.str "... with %d subsequent test%a skipped." n Pp.pp_plural n) | 0 -> () | _ -> assert false in test_results let skip_label test_case = Suite.{ test_case with fn = `Skip } let filter_test_case p test_case = match p with | None -> true | Some p -> ( let name, index = let tn = test_case.Suite.name in Test_name.(Safe_string.to_unescaped_string (name tn), index tn) in match p ~name ~index with `Run -> true | `Skip -> false) let filter_test_cases ~subst path test_cases = let filter_test_case = filter_test_case path in test_cases |> List.filter_map (fun tc -> if filter_test_case tc then Some tc else if subst then Some (skip_label tc) else None) let select_speed speed_level (test_case : 'a Suite.test_case as 'tc) : 'tc = if compare_speed_level test_case.speed_level speed_level >= 0 then test_case else Suite.{ test_case with fn = `Skip } let result t test args = let initial_backtrace_status = Printexc.backtrace_status () in if t.config#record_backtrace then Printexc.record_backtrace true; let start_time = P.time () in let speed_level = if t.config#quick_only then `Quick else `Slow in let test = List.map (select_speed speed_level) test in let+ results = perform_tests t test args in let time = P.time () -. start_time in let success = List.length (List.filter has_run results) in let failures = List.length (List.filter Run_result.is_failure results) in if t.config#record_backtrace then Printexc.record_backtrace initial_backtrace_status; Pp.{ time; success; failures; errors = List.rev t.errors } let list_registered_tests t () = Suite.tests t.suite |> List.map (fun t -> t.Suite.name) |> List.sort Test_name.compare |> Fmt.(list ~sep:(const string "\n") (pp_info t) stdout) let register (type a) (t : a t) (name, (ts : a test_case list)) : a t = let max_label = max t.max_label (String.length_utf8 name) in let test_details = List.mapi (fun index (doc, speed, test) -> let path = Test_name.v ~name ~index in let doc = if doc = "" || doc.[String.length doc - 1] = '.' then doc else doc ^ "." in let test a = protect_test path test a in (path, doc, speed, `Run test)) ts in let suite = List.fold_left (fun acc td -> match Suite.add acc td with | Ok acc -> acc | Error (`Duplicate_test_path path) -> Fmt.kstr Pp.user_error "Duplicate test path: `%s'" path) t.suite test_details in { t with suite; max_label } let register_all t cases = List.fold_left register t cases let run_tests t () args = let filter = t.config#filter in let suite = Suite.tests t.suite in let is_empty = filter_test_cases ~subst:false filter suite = [] in let+ result = if is_empty && Option.is_some filter then ( flush_all (); Fmt.(pf stderr) "%a\n" red "Invalid request (no tests to run, filter skipped everything)!"; exit 1) else let tests = filter_test_cases ~subst:true filter suite in result t tests args in (pp_suite_results t) Fmt.stdout result; result.failures let default_log_dir () = let fname_concat l = List.fold_left Filename.concat "" l in fname_concat [ P.getcwd (); "_build"; "_tests" ] type 'a with_options = 'a Config.with_options let list_tests (type a) (tl : a test list) = (* TODO: refactor [register_all] to not require dummy state *) let config = Config.apply_defaults ~default_log_dir:"" (Config.User.create ()) in let t = register_all (empty ~config ~trap_logs:false ~suite_name:"") tl in list_registered_tests t (); M.return () let run_with_args' (config : Config.User.t) name (type a) (args : a) (tl : a test list) = let config = Config.apply_defaults ~default_log_dir:(default_log_dir ()) config in let t = empty ~config ~trap_logs:(not config#verbose) ~suite_name:name in let t = register_all t tl in let+ test_failures = (* Only print inside the concurrency monad *) let* () = M.return () in let open Fmt in if config#ci = `Github_actions then pr "::group::{%a}\n" Suite.pp_name t.suite; pr "Testing %a.@," (Pp.quoted Fmt.(styled `Bold Suite.pp_name)) t.suite; pr "@[%a@]" (styled `Faint (fun ppf () -> pf ppf "This run has ID %a.@,@," (Pp.quoted string) t.run_id)) (); let r = run_tests t () args in if config#ci = `Github_actions then pr "::endgroup::\n"; r in match (test_failures, t.config#and_exit) with | 0, true -> exit 0 | 0, false -> () | _, true -> exit 1 | _, false -> raise Test_error let run' config name (tl : unit test list) = run_with_args' config name () tl let run_with_args ?and_exit ?verbose ?compact ?tail_errors ?quick_only ?show_errors ?json ?filter ?log_dir ?bail ?record_backtrace ?ci = Config.User.kcreate run_with_args' ?and_exit ?verbose ?compact ?tail_errors ?quick_only ?show_errors ?json ?filter ?log_dir ?bail ?record_backtrace ?ci let run = Config.User.kcreate run' end module V1 = struct include V1_types module Make = Make end alcotest-1.7.0/src/alcotest-engine/core.mli000066400000000000000000000015001437617477400206600ustar00rootroot00000000000000(* * Copyright (c) 2021 Craig Ferguson * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) include Core_intf.Core (** @inline *) alcotest-1.7.0/src/alcotest-engine/core_intf.ml000066400000000000000000000120211437617477400215270ustar00rootroot00000000000000(* * Copyright (c) 2013-2016 Thomas Gazagnaire * Copyright (c) 2021 Craig Ferguson * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) open! Import module V1_types = struct module type S = sig type return (** The return type of each test case run by Alcotest. For the standard {!Alcotest} module, [return = unit]. The concurrent backends [Alcotest_lwt] and [Alcotest_async] set [return = unit Lwt.t] and [return = Async_kernel.Deferred.t] respectively. *) type speed_level = [ `Quick | `Slow ] (** Speed level of a test. Tests marked as [`Quick] are always run. Tests marked as [`Slow] are skipped when the `-q` flag is passed. *) type 'a test_case = string * speed_level * ('a -> return) (** A test case is a UTF-8 encoded documentation string, a speed level and a function to execute. Typically, the testing function calls the helper functions provided below (such as [check] and [fail]). *) exception Test_error (** The exception return by {!run} in case of errors. *) val test_case : string -> speed_level -> ('a -> return) -> 'a test_case (** [test_case n s f] is the test case [n] running at speed [s] using the function [f]. *) type 'a test = string * 'a test_case list (** A test is a UTF-8 encoded name and a list of test cases. The name can be used for filtering which tests to run on the CLI. *) type 'a with_options = ?and_exit:bool -> ?verbose:bool -> ?compact:bool -> ?tail_errors:[ `Unlimited | `Limit of int ] -> ?quick_only:bool -> ?show_errors:bool -> ?json:bool -> ?filter:(name:string -> index:int -> [ `Run | `Skip ]) -> ?log_dir:string -> ?bail:bool -> ?record_backtrace:bool -> ?ci:Config.ci -> 'a (** The various options taken by the tests runners {!run} and {!run_with_args}: - [and_exit] (default [true]). Once the tests have completed, exit with return code [0] if all tests passed, otherwise [1]. - [verbose] (default [false]). Display the test std.out and std.err (rather than redirecting to a log file). - [compact] (default [false]). Compact the output of the tests. - [tail_errors] (default unlimited). Show only the last N lines of output of failed tests. - [quick_only] (default [false]). Don't run tests with the {{!speed_level} [`Slow] speed level}. - [show_errors] (default [false]). Display the test errors. - [json] (default [false]). Print test results in a JSON-compatible format. - [filter]. Filter tests according to [~name], the name of the test, and [~index], the number of the test case. - [log_dir] (default ["$PWD/_build/_tests/"]). The directory in which to log the output of the tests (if [verbose] is not set). - [bail] (default [false]). If true, stop running the tests after the first failure. - [record_backtrace] (default [true]). Enable backtrace recording before beginning testing. - [ci] (default auto-detected). Whether to enable specific logging for a CI system. *) val run : (string -> unit test list -> return) with_options val run_with_args : (string -> 'a -> 'a test list -> return) with_options end module type MAKER = functor (_ : Platform.MAKER) (M : Monad.S) -> sig include S with type return = unit M.t val list_tests : 'a test list -> return (** Print all of the test cases in a human-readable form *) val run' : Config.User.t -> string -> unit test list -> return (** Variant of {!run} that consumes a config value. *) val run_with_args' : Config.User.t -> string -> 'a -> 'a test list -> return (** Variant of {!run_with_args} that consumes a config value. *) end end module type Core = sig exception Check_error of unit Fmt.t exception Skip module V1 : sig module type S = V1_types.S module type MAKER = V1_types.MAKER module Make : MAKER (** Functor for building a tester that sequences tests of type [('a -> unit M.t)] within a given concurrency monad [M.t]. The [run] and [run_with_args] functions must be scheduled in a global event loop. Intended for use by the {!Alcotest_lwt} and {!Alcotest_async} backends. *) end end alcotest-1.7.0/src/alcotest-engine/dune000066400000000000000000000010061437617477400201040ustar00rootroot00000000000000(rule (target callsite_loc.ml) (deps callsite_loc.412.ml) (enabled_if (>= %{ocaml_version} 4.12.0)) (action (copy %{deps} %{target}))) (rule (target callsite_loc.ml) (enabled_if (< %{ocaml_version} 4.12.0)) (action (write-file %{target} "let get ?__FUNCTION__ () =\n ignore __FUNCTION__;\n None\n"))) (library (name alcotest_engine) (public_name alcotest.engine) (libraries alcotest.stdlib_ext fmt astring cmdliner fmt.cli re stdlib-shims uutf) (preprocess future_syntax)) alcotest-1.7.0/src/alcotest-engine/import.ml000066400000000000000000000000341437617477400210720ustar00rootroot00000000000000include Alcotest_stdlib_ext alcotest-1.7.0/src/alcotest-engine/log_trap.ml000066400000000000000000000064601437617477400214000ustar00rootroot00000000000000open Astring open Model open! Import include Log_trap_intf module Make (Promise : Monad.EXTENDED) (Platform : Platform.S with type 'a promise := 'a Promise.t) = struct open Promise.Syntax type state = { root : string; uuid : string; suite_name : string; has_alias : bool; } type t = Inactive | Active of state (** Take a string path and collapse a leading [$HOME] path segment to [~]. *) let maybe_collapse_home path = match Platform.home_directory () with | Error _ -> path | Ok home -> ( (* Astring doesn't have [cut_prefix]. *) match String.is_prefix ~affix:home path with | false -> path | true -> let tail = String.Sub.to_string (String.sub ~start:(String.length home) path) in "~" ^ tail) let inactive = Inactive let active ~root ~uuid ~suite_name = Platform.prepare_log_trap ~root ~uuid ~name:suite_name; let has_alias = Platform.file_exists (Filename.concat root suite_name) in Active { root; uuid; suite_name; has_alias } let pp_path = Fmt.using maybe_collapse_home Fmt.string let iter_lines fpath ~f = let ic = open_in fpath in try while true do f (input_line ic) done; assert false with End_of_file -> close_in ic (** Show the last lines of a log file. *) let pp_tail max_lines fpath ppf = match max_lines with | `Unlimited -> iter_lines fpath ~f:(Fmt.pf ppf "%s@\n") | `Limit limit -> let rev_lines = ref [] in iter_lines fpath ~f:(fun l -> rev_lines := l :: !rev_lines); let selected_lines = List.rev_head limit !rev_lines in let omitted_count = List.length !rev_lines - List.length selected_lines in let display_lines = if omitted_count = 0 then selected_lines else Fmt.str "... (omitting %i line%a)" omitted_count Pp.pp_plural omitted_count :: selected_lines in ListLabels.iter display_lines ~f:(Fmt.pf ppf "%s@\n") let log_dir ~via_symlink { suite_name; uuid; root; has_alias } = let via_symlink = via_symlink && has_alias in Filename.concat root (if via_symlink then suite_name else uuid) let output_fpath ~via_symlink t tname = Filename.concat (log_dir ~via_symlink t) (Test_name.file tname) let active_or_exn = function | Active t -> t | Inactive -> failwith "internal error: no log location" let pp_current_run_dir t ppf = let t = active_or_exn t in pp_path ppf (log_dir ~via_symlink:true t) let pp_log_location t tname ppf = let t = active_or_exn t in let path = output_fpath ~via_symlink:true t tname in pp_path ppf path let recover_logs t ~tail tname = match t with | Inactive -> None | Active t -> ( let fpath = output_fpath ~via_symlink:false t tname in match Platform.file_exists fpath with | false -> None | true -> Some (fun ppf -> pp_tail tail fpath ppf)) let with_captured_logs t tname f x = match t with | Inactive -> f x | Active t -> let fd = Platform.open_write_only (output_fpath ~via_symlink:false t tname) in let* () = Promise.return () in let+ a = Platform.with_redirect fd (fun () -> f x) in Platform.close fd; a end alcotest-1.7.0/src/alcotest-engine/log_trap.mli000066400000000000000000000000561437617477400215440ustar00rootroot00000000000000include Log_trap_intf.Log_trap (** @inline *) alcotest-1.7.0/src/alcotest-engine/log_trap_intf.ml000066400000000000000000000037711437617477400224220ustar00rootroot00000000000000open! Import open Model (** Running tests have their output hidden by default to avoid cluttering the Alcotest display with irrelevant output. However, we (usually) want to keep the logs on disk so that we can re-display them if a test fails. Logs are stored with the following structure: {[ ├── E0965BF9/... ├── 6DDB68D5/ ;; ID for each test run │ │ │ ├── alpha.000.output ;; ... containing files for individual tests │ ├── alpha.001.output ;; with format ..output. │ └── beta.000.output │ ├── latest/ ;; Symlink to most recent UUID └── / ;; Symlink to most recent UUID ]} *) module type S = sig type 'a promise type t val inactive : t val active : root:string -> uuid:string -> suite_name:string -> t val with_captured_logs : t -> Test_name.t -> ('a -> 'b promise) -> 'a -> 'b promise (** Capture all logs for a given test run. *) val recover_logs : t -> tail:[ `Unlimited | `Limit of int ] -> Test_name.t -> (Format.formatter -> unit) option (** Print the logs for a given test to the given formatter, if they exist. [tail] determines whether to show all lines in the captured log or just a suffix of them. *) val pp_current_run_dir : t -> Format.formatter -> unit (** Print the folder containing all captured traces for the current test run. Raises an exception if traces are not being recorded. *) val pp_log_location : t -> Test_name.t -> Format.formatter -> unit (** Print the file containing the trace of a particular test. Raises an exception if traces are not being recorded. *) end module type Log_trap = sig module type S = S module Make (Promise : Monad.EXTENDED) (_ : Platform.S with type 'a promise := 'a Promise.t) : S with type 'a promise := 'a Promise.t end alcotest-1.7.0/src/alcotest-engine/model.ml000066400000000000000000000073301437617477400206660ustar00rootroot00000000000000open! Import type speed_level = [ `Quick | `Slow ] module Test_name : sig type t val v : name:string -> index:int -> t val name : t -> Safe_string.t val index : t -> int val pp : t Fmt.t (** Pretty-print the unescaped test-case name *) val file : t -> string (** An escaped form of the test name with [.output] suffix. *) val length : t -> int (** The approximate number of terminal columns consumed by [pp_name]. *) val compare : t -> t -> int (** Order lexicographically by name, then by index. *) end = struct type t = { name : Safe_string.t; file : string; index : int } let index { index; _ } = index let v ~name ~index = let name = Safe_string.v name in let file = let name = match Safe_string.to_string name with "" -> "" | n -> n ^ "." in Fmt.str "%s%03d.output" name index in { name; file; index } let pp = Fmt.using (fun { name; _ } -> name) Safe_string.pp let name { name; _ } = name let file { file; _ } = file let length = name >> Safe_string.length let compare t t' = match Safe_string.compare t.name t'.name with | 0 -> (compare : int -> int -> int) t.index t'.index | n -> n end module Run_result = struct type t = [ `Ok | `Exn of Test_name.t * string * unit Fmt.t | `Error of Test_name.t * unit Fmt.t | `Skip | `Todo of string ] (** [is_failure] holds for test results that are error states. *) let is_failure : t -> bool = function | `Ok | `Skip -> false | `Error _ | `Exn _ | `Todo _ -> true end module Suite (M : Monad.S) : sig type 'a t type 'a test_fn = [ `Skip | `Run of 'a -> Run_result.t M.t ] type 'a test_case = { name : Test_name.t; speed_level : speed_level; fn : 'a test_fn; } val v : name:string -> (_ t, [> `Empty_name ]) result (** Construct a new suite, given a non-empty [name]. Test cases must be added with {!add}. *) val name : _ t -> string (** An escaped form of the suite name. *) val pp_name : _ t Fmt.t (** Pretty-print the unescaped suite name. *) val add : 'a t -> Test_name.t * string * speed_level * 'a test_fn -> ('a t, [ `Duplicate_test_path of string ]) result val tests : 'a t -> 'a test_case list val doc_of_test_name : 'a t -> Test_name.t -> string end = struct module String_set = Set.Make (String) type 'a test_fn = [ `Skip | `Run of 'a -> Run_result.t M.t ] type 'a test_case = { name : Test_name.t; speed_level : speed_level; fn : 'a test_fn; } type 'a t = { name : Safe_string.t; tests : 'a test_case list; (* caches computed from the library values. *) filepaths : String_set.t; doc : (Test_name.t, string) Hashtbl.t; } let v ~name = match String.length name with | 0 -> Error `Empty_name | _ -> let name = Safe_string.v name in let tests = [] in let filepaths = String_set.empty in let doc = Hashtbl.create 0 in Ok { name; tests; filepaths; doc } let name { name; _ } = Safe_string.to_string name let pp_name ppf { name; _ } = Safe_string.pp ppf name let check_path_is_unique t tname = match String_set.mem (Test_name.file tname) t.filepaths with | false -> Ok () | true -> Error (`Duplicate_test_path (Fmt.to_to_string Test_name.pp tname)) let add t (tname, doc, speed_level, fn) = match check_path_is_unique t tname with | Error _ as e -> e | Ok () -> let tests = { name = tname; speed_level; fn } :: t.tests in let filepaths = String_set.add (Test_name.file tname) t.filepaths in Hashtbl.add t.doc tname doc; Ok { t with tests; filepaths } let tests t = List.rev t.tests let doc_of_test_name t path = try Hashtbl.find t.doc path with Not_found -> "" end alcotest-1.7.0/src/alcotest-engine/monad.ml000066400000000000000000000027441437617477400206700ustar00rootroot00000000000000(* * Copyright (c) 2019 Craig Ferguson * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) include Monad_intf module Identity = struct type 'a t = 'a let return x = x let bind x f = f x let catch f on_error = match f () with x -> x | exception ex -> on_error ex end module Extend (M : S) = struct include M module Syntax = struct let ( >>= ) = M.bind let ( >|= ) x f = x >>= fun y -> M.return (f y) let ( let* ) = ( >>= ) let ( let+ ) = ( >|= ) end open Syntax module List = struct let fold_map_s f init l = let rec inner acc results = function | [] -> M.return (acc, List.rev results) | hd :: tl -> let* acc, r = f acc hd in (inner [@ocaml.tailcall]) acc (r :: results) tl in inner init [] l end end alcotest-1.7.0/src/alcotest-engine/monad.mli000066400000000000000000000015021437617477400210300ustar00rootroot00000000000000(* * Copyright (c) 2019 Craig Ferguson * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) include Monad_intf.Monad (** @inline *) alcotest-1.7.0/src/alcotest-engine/monad_intf.ml000066400000000000000000000027671437617477400217150ustar00rootroot00000000000000(* * Copyright (c) 2019-2021 Craig Ferguson * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) module type S = sig type 'a t val return : 'a -> 'a t val bind : 'a t -> ('a -> 'b t) -> 'b t val catch : (unit -> 'a t) -> (exn -> 'a t) -> 'a t end module type EXTENDED = sig include S module Syntax : sig val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t val ( >|= ) : 'a t -> ('a -> 'b) -> 'b t end module List : sig val fold_map_s : ('acc -> 'a -> ('acc * 'b) t) -> 'acc -> 'a list -> ('acc * 'b list) t end end module type Monad = sig module type S = S module type EXTENDED = EXTENDED module Identity : S with type 'a t = 'a module Extend (M : S) : EXTENDED with type 'a t = 'a M.t end alcotest-1.7.0/src/alcotest-engine/platform.ml000066400000000000000000000036431437617477400214150ustar00rootroot00000000000000open! Import module type S = sig val time : unit -> float (** [time ()] returns the current timestamp, used to measure the duration of a testrun. *) val getcwd : unit -> string (** [getcwd ()] returns the current working directory. *) type 'a promise (** The type of monadic actions handled by {!with_redirect}. *) val stdout_isatty : unit -> bool (** Return [true] if standard output refers to a terminal or console window, [false] otherwise. *) val stdout_columns : unit -> int option (** [stdout_columns ()] is the current width of [stdout] in columns, or [None] if no width can be determined (e.g. [stdout] is not a TTY). *) val setup_std_outputs : ?style_renderer:Fmt.style_renderer -> ?utf_8:bool -> unit -> unit (** [setup_std_outputs ~style_renderer ~utf_8 ()] is called at startup of alcotest and sets up the standard streams for colored output. *) val log_trap_supported : bool (** Whether or not the test runner should trap test logs. The following functions are used iff this is set to [true]. *) val prepare_log_trap : root:string -> uuid:string -> name:string -> unit (** [prepare ~root ~uuid ~name] is called before test suite execution. - [root] is the directory used for log capturing; - [uuid] is the unique test execution ID; - [name] is the suite name. *) type file_descriptor val file_exists : string -> bool val open_write_only : string -> file_descriptor val close : file_descriptor -> unit val with_redirect : file_descriptor -> (unit -> 'a promise) -> 'a promise (** [with_redirect fd f] is called for each test. On Unix, it deals with redirection of standard streams to the [fd]. *) val home_directory : unit -> (string, [ `Msg of string ]) result (** [home_directory ()] is the current user's HOME directory, if it exists. *) end module type MAKER = functor (M : Monad.S) -> S with type 'a promise := 'a M.t alcotest-1.7.0/src/alcotest-engine/pp.ml000066400000000000000000000201731437617477400202050ustar00rootroot00000000000000(* * Copyright (c) 2013-2016 Thomas Gazagnaire * Copyright (c) 2019 Craig Ferguson * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) include Pp_intf include Pp_intf.Types open! Import open Model let map_theta t ~f ppf = f (fun ppf () -> t ppf) ppf () let pp_plural ppf x = Fmt.pf ppf (if x < 2 then "" else "s") let colour_of_tag = function | `Ok -> `Green | `Fail -> `Red | `Skip | `Todo | `Assert -> `Yellow let string_of_tag = function | `Ok -> "OK" | `Fail -> "FAIL" | `Skip -> "SKIP" | `Todo -> "TODO" | `Assert -> "ASSERT" let pp_tag ~wrapped ppf typ = let colour = colour_of_tag typ in let tag = string_of_tag typ in let tag = if wrapped then "[" ^ tag ^ "]" else tag in Fmt.(styled colour string) ppf tag let tag = pp_tag ~wrapped:false module Make (P : sig val stdout_columns : unit -> int option end) = struct include Types let terminal_width = lazy (match P.stdout_columns () with Some w -> w | None -> 80) let rresult_error ppf = function | `Error (_, e) -> Fmt.pf ppf "%a@," e () | `Exn (_, n, e) -> Fmt.pf ppf "[%s] @[%a@]" n e () | `Ok | `Todo _ | `Skip -> () (* Colours *) let color c ppf fmt = Fmt.(styled c string) ppf fmt let red_s fmt = color `Red fmt let red ppf fmt = Fmt.kstr (fun str -> red_s ppf str) fmt let green_s fmt = color `Green fmt let yellow_s fmt = color `Yellow fmt let left_gutter = 2 let left_tag = 14 let left_total = left_gutter + left_tag let left nb pp ppf a = let s = Fmt.to_to_string pp a in let nb = nb - String.length_utf8 s in if nb <= 0 then pp ppf a else ( pp ppf a; Fmt.string ppf (String.v ~len:nb (fun _ -> ' '))) let pp_test_name ~max_label ppf tname = let name_len = Test_name.length tname in let index = Test_name.index tname in let padding = match max_label + 8 - name_len with | n when n <= 0 -> "" | n -> String.v ~len:n (fun _ -> ' ') in Fmt.pf ppf "%a%s%3d" Fmt.(styled `Cyan Test_name.pp) tname padding index let info ?(available_width = Lazy.force terminal_width) ~max_label ~doc_of_test_name ppf tname = let pp_test_name ppf = Fmt.pf ppf "%a " (pp_test_name ~max_label) tname in let test_doc = let test_doc = doc_of_test_name tname in let available_width = pp_test_name Format.str_formatter; let used_width = String.length_utf8 (Format.flush_str_formatter ()) in available_width - used_width in if String.length_utf8 test_doc <= available_width then test_doc else String.prefix_utf8 (available_width - 3) test_doc ^ "..." in Fmt.pf ppf "%t%s" pp_test_name test_doc let tag_of_result = function | `Ok -> `Ok | `Exn _ | `Error _ -> `Fail | `Skip -> `Skip | `Todo _ -> `Todo let pp_result ppf result = let tag = tag_of_result result in left left_tag (pp_tag ~wrapped:true) ppf tag let pp_result_compact ppf result = let colour = result |> tag_of_result |> colour_of_tag in let char = match result with | `Ok -> '.' | `Exn _ | `Error _ -> 'F' | `Skip -> 'S' | `Todo _ -> 'T' in Fmt.(styled colour char) ppf char let left_padding ~with_selector = let open Fmt in (if with_selector then const (styled `Bold (styled `Red char)) '>' else const char ' ') ++ const char ' ' let pp_result_full ~max_label ~doc_of_test_name ~selector_on_failure ppf (path, result) = let with_selector = selector_on_failure && Run_result.is_failure result in (left_padding ~with_selector) ppf (); pp_result ppf result; let available_width = Lazy.force terminal_width - left_total in (info ~available_width ~max_label ~doc_of_test_name) ppf path; () let event_line ~margins ~max_label ~doc_of_test_name ppf = function | `Result (p, r) -> pp_result ppf r; (info ~available_width:(Lazy.force terminal_width - margins - left_total) ~max_label ~doc_of_test_name) ppf p | _ -> assert false let event ~isatty ~compact ~max_label ~doc_of_test_name ~selector_on_failure ~tests_so_far ppf event = match (compact, isatty, event) with | true, _, `Start _ | _, false, `Start _ -> () | false, true, `Start tname -> Fmt.( left_padding ~with_selector:false ++ const (left left_tag yellow_s) "..." ++ const (info ~available_width:(Lazy.force terminal_width - left_total) ~max_label ~doc_of_test_name) tname) ppf () | true, _, `Result (_, r) -> pp_result_compact ppf r; (* Wrap compact output to terminal width manually *) if (tests_so_far + 1) mod Lazy.force terminal_width = 0 then Format.pp_force_newline ppf (); () | false, _, `Result (tname, r) -> if isatty then Fmt.pf ppf "\r"; Fmt.pf ppf "%a@," (pp_result_full ~max_label ~doc_of_test_name ~selector_on_failure) (tname, r) let pp_suite_errors ~show_all = function | [] -> Fmt.nop | x :: _ as xs -> (if show_all then xs else [ x ]) |> Fmt.concat let quoted f = Fmt.(const char '`' ++ f ++ const char '\'') let with_surrounding_box (type a) (f : a Fmt.t) : a Fmt.t = fun ppf a -> (* Peek at the value being pretty-printed to determine the length of the box we're going to need. Fortunately, this will not include ANSII colour escapes. *) let true_width = Fmt.kstr String.length_utf8 "| %a |" f a in let min_width = Lazy.force terminal_width in let width = max min_width true_width in let right_padding = String.v ~len:(width - true_width) (fun _ -> ' ') in let s = Fmt.(const (styled `Faint string)) in let bars = List.init (width - 2) (fun _ -> "─") |> String.concat in let top = s ("┌" ^ bars ^ "┐") and mid = Fmt.(s "│ " ++ f ++ s (right_padding ^ " │")) and bottom = s ("└" ^ bars ^ "┘") in Fmt.(top ++ cut ++ mid ++ cut ++ bottom ++ cut) ppf a let horizontal_rule (type a) ppf (_ : a) = let open Fmt in (const string " " ++ const (styled `Faint string) (List.init (Lazy.force terminal_width - 2) (fun _ -> "─") |> String.concat) ++ cut) ppf () let pp_full_logs ppf log_dir = Fmt.pf ppf "Full test results in %t.@," (map_theta ~f:Fmt.(styled `Cyan >> quoted) log_dir) let pp_summary ppf r = let pp_failures ppf = function | 0 -> green_s ppf "Test Successful" | n -> red ppf "%d failure%a!" n pp_plural n in Fmt.pf ppf "%a in %.3fs. %d test%a run.@," pp_failures r.failures r.time r.success pp_plural r.success let suite_results ~log_dir config ppf r = let print_summary = (not config#compact) || r.failures > 0 in match config#json with | true -> (* Return the json for the api, dirty out, to avoid new dependencies *) Fmt.pf ppf {|{ "success": %i, "failures": %i, "time": %f } |} r.success r.failures r.time | false -> Format.pp_force_newline ppf (); Format.pp_open_vbox ppf 0; if config#compact then Fmt.cut ppf (); (pp_suite_errors ~show_all:(config#verbose || config#show_errors) r.errors) ppf (); if print_summary then ( if not config#verbose then pp_full_logs ppf log_dir; pp_summary ppf r); Format.pp_close_box ppf () let user_error msg = Fmt.epr "%a: %s\n" Fmt.(styled `Red string) "ERROR" msg; exit 1 end alcotest-1.7.0/src/alcotest-engine/pp.mli000066400000000000000000000016111437617477400203520ustar00rootroot00000000000000(* * Copyright (c) 2013-2016 Thomas Gazagnaire * Copyright (c) 2019 Craig Ferguson * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) include Pp_intf.Pp (** @inline *) alcotest-1.7.0/src/alcotest-engine/pp_intf.ml000066400000000000000000000055501437617477400212270ustar00rootroot00000000000000(* * Copyright (c) 2013-2016 Thomas Gazagnaire * Copyright (c) 2019 Craig Ferguson * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) open Model type theta = Format.formatter -> unit (** Type corresponding to a [%t] placeholder. *) module Types = struct type event = [ `Result of Test_name.t * Run_result.t | `Start of Test_name.t ] type result = { success : int; failures : int; time : float; errors : unit Fmt.t list; } end module type Make_arg = sig val stdout_columns : unit -> int option end module type S = sig include module type of Types val info : ?available_width:int -> max_label:int -> doc_of_test_name:(Test_name.t -> string) -> Test_name.t Fmt.t val rresult_error : Run_result.t Fmt.t val event_line : margins:int -> max_label:int -> doc_of_test_name:(Test_name.t -> string) -> [ `Result of Test_name.t * [< Run_result.t ] | `Start of Test_name.t ] Fmt.t val event : isatty:bool -> compact:bool -> max_label:int -> doc_of_test_name:(Test_name.t -> string) -> selector_on_failure:bool -> tests_so_far:int -> event Fmt.t val suite_results : log_dir:theta -> < verbose : bool ; show_errors : bool ; json : bool ; compact : bool ; .. > -> result Fmt.t val quoted : 'a Fmt.t -> 'a Fmt.t (** Wraps a formatter with `GNU-style quotation marks'. *) val with_surrounding_box : 'a Fmt.t -> 'a Fmt.t (** Wraps a formatter with a Unicode box with width given by {!_.stdout_columns}. Uses box-drawing characters from code page 437. *) val horizontal_rule : _ Fmt.t (** Horizontal rule of length {!_.stdout_columns}. Uses box-drawing characters from code page 437. *) val user_error : string -> _ (** Raise a user error, then fail. *) end module type Pp = sig val tag : [ `Ok | `Fail | `Skip | `Todo | `Assert ] Fmt.t val map_theta : theta -> f:(unit Fmt.t -> unit Fmt.t) -> theta val pp_plural : int Fmt.t (** This is for adding an 's' to words that should be pluralized, e.g. {[ let n = List.length items in Fmt.pr "Found %i item%a." n pp_plural n ]} *) module Make (_ : Make_arg) : S end alcotest-1.7.0/src/alcotest-engine/safe_string.ml000066400000000000000000000021041437617477400220640ustar00rootroot00000000000000open! Import let escape str = let add_codepoint buf uchar = Uchar.to_int uchar |> Fmt.str "U+%04X" |> Buffer.add_string buf in let buf = Buffer.create (String.length str * 2) in let get_normalized_char _ _ u = match u with | `Uchar u -> if Uchar.is_char u then match Uchar.to_char u with | ('A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '_' | '-' | ' ' | '.') as c -> Buffer.add_char buf c | '/' | '\\' -> Buffer.add_char buf '-' | _ -> add_codepoint buf u else add_codepoint buf u | `Malformed _ -> Uutf.Buffer.add_utf_8 buf Uutf.u_rep in Uutf.String.fold_utf_8 get_normalized_char () str; Buffer.contents buf type t = { raw : string; escaped : string } let v raw = { raw; escaped = escape raw } let to_string { escaped; _ } = escaped let to_unescaped_string { raw; _ } = raw let pp = Fmt.using (fun { raw; _ } -> raw) Fmt.string let length { raw; _ } = String.length_utf8 raw let equal t t' = String.equal t.escaped t'.escaped let compare t t' = String.compare t.escaped t'.escaped alcotest-1.7.0/src/alcotest-engine/safe_string.mli000066400000000000000000000011031437617477400222330ustar00rootroot00000000000000type t (** A UTF-8 encoded string that has been made safe for use in filesystems by escaping any "unsafe" characters as their [U+XXXX] notational form. *) val v : string -> t val to_string : t -> string (** Get the escaped form of the given {!Safe_string}. *) val to_unescaped_string : t -> string (** Get the raw, unescaped string initially passed to [v]. *) val pp : t Fmt.t (** Pretty-print the unescaped string. *) val length : t -> int (** The approximate number of terminal columns consumed by [pp_name]. *) val equal : t -> t -> bool val compare : t -> t -> int alcotest-1.7.0/src/alcotest-engine/test.ml000066400000000000000000000147021437617477400205460ustar00rootroot00000000000000(* * Copyright (c) 2013-2016 Thomas Gazagnaire * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) module type TESTABLE = sig type t val pp : t Fmt.t val equal : t -> t -> bool end type 'a testable = (module TESTABLE with type t = 'a) let pp (type a) (t : a testable) = let (module T) = t in T.pp let equal (type a) (t : a testable) = let (module T) = t in T.equal let isnan f = FP_nan = classify_float f let testable (type a) (pp : a Fmt.t) (equal : a -> a -> bool) : a testable = let module M = struct type t = a let pp = pp let equal = equal end in (module M) let int32 = testable Fmt.int32 ( = ) let int64 = testable Fmt.int64 ( = ) let int = testable Fmt.int ( = ) let float eps = let same x y = (isnan x && isnan y) (* compare infinities *) || x = y || abs_float (x -. y) <= eps in testable Fmt.float same let char = let pp_char ppf x = Fmt.pf ppf "%C" x in testable pp_char ( = ) let string = let pp_string ppf x = Fmt.pf ppf "%S" x in testable pp_string ( = ) let bytes = testable (fun fmt bytes -> Fmt.fmt "%S" fmt (Bytes.to_string bytes)) ( = ) let bool = testable Fmt.bool ( = ) let unit = testable (Fmt.any "()") ( = ) let list e = let rec eq l1 l2 = match (l1, l2) with | x :: xs, y :: ys -> equal e x y && eq xs ys | [], [] -> true | _ -> false in testable (Fmt.Dump.list (pp e)) eq let slist (type a) (a : a testable) compare = let l = list a in let eq l1 l2 = equal l (List.sort compare l1) (List.sort compare l2) in testable (pp l) eq let array e = let eq a1 a2 = let m, n = Array.(length a1, length a2) in let rec go i = i = m || (equal e a1.(i) a2.(i) && go (i + 1)) in m = n && go 0 in testable (Fmt.Dump.array (pp e)) eq let pair a b = let eq (a1, b1) (a2, b2) = equal a a1 a2 && equal b b1 b2 in testable (Fmt.Dump.pair (pp a) (pp b)) eq let triple a b c = let eq (a1, b1, c1) (a2, b2, c2) = equal a a1 a2 && equal b b1 b2 && equal c c1 c2 in let pp = Fmt.( parens (using (fun (x, _, _) -> x) (box (pp a)) ++ comma ++ using (fun (_, x, _) -> x) (box (pp b)) ++ comma ++ using (fun (_, _, x) -> x) (box (pp c)))) in testable pp eq let option e = let eq x y = match (x, y) with | Some a, Some b -> equal e a b | None, None -> true | _ -> false in testable (Fmt.Dump.option (pp e)) eq let result a e = let eq x y = match (x, y) with | Ok x, Ok y -> equal a x y | Error x, Error y -> equal e x y | _ -> false in testable (Fmt.Dump.result ~ok:(pp a) ~error:(pp e)) eq let of_pp pp = testable pp ( = ) let pass (type a) = let module M = struct type t = a let pp fmt _ = Fmt.string fmt "Alcotest.pass" let equal _ _ = true end in (module M : TESTABLE with type t = M.t) let reject (type a) = let module M = struct type t = a let pp fmt _ = Fmt.string fmt "Alcotest.reject" let equal _ _ = false end in (module M : TESTABLE with type t = M.t) let show_assert = function | "" -> () | msg -> Fmt.(flush stdout) () (* Flush any test stdout preceding the assert *); Format.eprintf "%a %s\n%!" Pp.tag `Assert msg let check_err fmt = raise (Core.Check_error fmt) module Source_code_position = struct type here = Lexing.position type pos = string * int * int * int end type 'a extra_info = ?here:Source_code_position.here -> ?pos:Source_code_position.pos -> 'a let pp_location = let pp = Fmt.styled `Bold (fun ppf (f, l, c) -> Fmt.pf ppf "File \"%s\", line %d, character %d:@," f l c) in fun ?here ?pos ppf -> match (here, pos) with | Some (here : Source_code_position.here), _ -> pp ppf (here.pos_fname, here.pos_lnum, here.pos_cnum - here.pos_bol) | _, Some (fname, lnum, cnum, _) -> pp ppf (fname, lnum, cnum) | None, None -> () let check (type a) ?here ?pos (t : a testable) msg (expected : a) (actual : a) = show_assert msg; if not (equal t expected actual) then let open Fmt in let s = const string in let pp_error = match msg with | "" -> nop | _ -> const Pp.tag `Fail ++ s (" " ^ msg) ++ cut and pp_expected ppf () = Fmt.pf ppf " Expected: `%a'" (styled `Green (pp t)) expected; Format.pp_print_if_newline ppf (); Fmt.cut ppf (); () and pp_actual ppf () = Fmt.pf ppf " Received: `%a'" (styled `Red (pp t)) actual and here, pos = match (here, pos) with | None, None -> (Callsite_loc.get (), None) | _ -> (here, pos) in raise (Core.Check_error Fmt.( vbox ((fun ppf () -> pp_location ?here ?pos ppf) ++ pp_error ++ cut ++ pp_expected ++ cut ++ pp_actual) ++ cut)) let check' ?here ?pos t ~msg ~expected ~actual = check ?here ?pos t msg expected actual let fail ?here ?pos msg = show_assert msg; check_err (fun ppf () -> Fmt.pf ppf "%t%a %s" (pp_location ?here ?pos) Pp.tag `Fail msg) let failf ?here ?pos fmt = Fmt.kstr (fun msg -> fail ?here ?pos msg) fmt let neg t = testable (pp t) (fun x y -> not (equal t x y)) let collect_exception f = try f (); None with e -> Some e let check_raises ?here ?pos msg exn f = show_assert msg; match collect_exception f with | None -> check_err (fun ppf () -> Fmt.pf ppf "%t%a %s: expecting %s, got nothing." (pp_location ?here ?pos) Pp.tag `Fail msg (Printexc.to_string exn)) | Some e -> if e <> exn then check_err (fun ppf () -> Fmt.pf ppf "%t%a %s: expecting %s, got %s." (pp_location ?here ?pos) Pp.tag `Fail msg (Printexc.to_string exn) (Printexc.to_string e)) let skip () = raise Core.Skip let () = at_exit (Format.pp_print_flush Format.err_formatter) alcotest-1.7.0/src/alcotest-engine/test.mli000066400000000000000000000121301437617477400207100ustar00rootroot00000000000000(* * Copyright (c) 2013 Thomas Gazagnaire * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) (** {1 Testable values} The following combinators represent types that can be used with the {!check} functions below. *) (** [TESTABLE] provides an abstract description for testable values. *) module type TESTABLE = sig type t (** The type to test. *) val pp : t Fmt.t (** A way to pretty-print the value. *) val equal : t -> t -> bool (** Test for equality between two values. *) end type 'a testable = (module TESTABLE with type t = 'a) (** The type for testable values. *) val testable : 'a Fmt.t -> ('a -> 'a -> bool) -> 'a testable (** [testable pp eq] is a new {!type-testable} with the pretty-printer [pp] and equality [eq]. *) val pp : 'a testable -> 'a Fmt.t (** [pp t] is [t]'s pretty-printer. *) val equal : 'a testable -> 'a -> 'a -> bool (** [equal t] is [t]'s equality. *) val bool : bool testable (** [bool] tests booleans. *) val int : int testable (** [int] tests integers. *) val int32 : int32 testable (** [int32] tests 32-bit integers. *) val int64 : int64 testable (** [int64] tests 64-bit integers. *) val float : float -> float testable (** [float] tests floats with specified absolute error. *) val char : char testable (** [char] tests characters. *) val string : string testable (** [string] tests OCaml strings. *) val bytes : bytes testable (** [bytes] tests OCaml bytes. *) val unit : unit testable (** [unit] tests unit values (useful for functions with side-effects). *) val list : 'a testable -> 'a list testable (** [list t] tests lists of [t]s. *) val slist : 'a testable -> ('a -> 'a -> int) -> 'a list testable (** [slist t comp] tests sorted lists of [t]s. The list are sorted using [comp]. *) val array : 'a testable -> 'a array testable (** [array t] tests arrays of [t]s. *) val option : 'a testable -> 'a option testable (** [option t] tests optional [t]s. *) val result : 'a testable -> 'e testable -> ('a, 'e) result testable (** [result t e] tests [t]s on success and [e]s on failure. *) val pair : 'a testable -> 'b testable -> ('a * 'b) testable (** [pair a b] tests pairs of [a]s and [b]s. *) val triple : 'a testable -> 'b testable -> 'c testable -> ('a * 'b * 'c) testable (** [triple a b c] tests triples of [a]s, [b]s and [c]s. *) val of_pp : 'a Fmt.t -> 'a testable (** [of_pp pp] tests values which can be printed using [pp] and compared using {!Stdlib.compare} *) val pass : 'a testable (** [pass] tests values of any type and always succeeds. *) val reject : 'a testable (** [reject] tests values of any type and always fails. *) val neg : 'a testable -> 'a testable (** [neg t] is [t]'s negation: it is [true] when [t] is [false] and it is [false] when [t] is [true]. *) (** {1 Assertion functions} Functions for asserting various properties within unit-tests. A failing assertion will cause the testcase to fail immediately. *) module Source_code_position : sig type here = Lexing.position (** Location information passed via a [~here] argument, intended for use with a PPX such as {{:https://github.com/janestreet/ppx_here} [ppx_here]}. *) type pos = string * int * int * int (** Location information passed via a [~pos] argument, intended for use with the [__POS__] macro provided by the standard library. See the documentation of [__POS__] for more information. *) end type 'a extra_info = ?here:Source_code_position.here -> ?pos:Source_code_position.pos -> 'a (** The assertion functions optionally take information about the {i location} at which they are called in the source code. This is used for giving more descriptive error messages in the case of failure. *) val check : ('a testable -> string -> 'a -> 'a -> unit) extra_info (** Check that two values are equal. If [check] isn't in a tail-call position, Alcotest may guess the location of the check. Otherwise, use {!extra_info} to report the location. *) val check' : ('a testable -> msg:string -> expected:'a -> actual:'a -> unit) extra_info (** Check that two values are equal (labeled variant of {!check}). *) val fail : (string -> 'a) extra_info (** Simply fail. *) val failf : (('a, Format.formatter, unit, 'b) format4 -> 'a) extra_info (** Simply fail with a formatted message. *) val check_raises : (string -> exn -> (unit -> unit) -> unit) extra_info (** Check that an exception is raised. *) val skip : unit -> 'a (** Skip the current test case. *) alcotest-1.7.0/src/alcotest-lwt/000077500000000000000000000000001437617477400165725ustar00rootroot00000000000000alcotest-1.7.0/src/alcotest-lwt/alcotest_lwt.ml000066400000000000000000000025761437617477400216420ustar00rootroot00000000000000(* * Copyright (c) 2017 Thomas Gazagnaire * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) let run_test fn args = let async_ex, async_waker = Lwt.wait () in let handle_exn ex = Logs.debug (fun f -> f "Uncaught async exception: %a" Fmt.exn ex); if Lwt.state async_ex = Lwt.Sleep then Lwt.wakeup_exn async_waker ex in Lwt.async_exception_hook := handle_exn; Lwt_switch.with_switch (fun sw -> Lwt.pick [ fn sw args; async_ex ]) module V1 = struct module Tester = Alcotest_engine.V1.Cli.Make (Alcotest.Unix_platform) (Lwt) include Tester let test_case_sync n s f = test_case n s (fun x -> Lwt.return (f x)) let test_case n s f = test_case n s (run_test f) end include V1 alcotest-1.7.0/src/alcotest-lwt/alcotest_lwt.mli000066400000000000000000000020251437617477400220000ustar00rootroot00000000000000(* * Copyright (c) 2017 Thomas Gazagnaire * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) (** [Alcotest_lwt] enables testing functions which return an Lwt promise. {!run} returns a promise that runs the tests when scheduled, catching any asynchronous exceptions thrown by the tests. *) include Alcotest_lwt_intf.Alcotest_lwt alcotest-1.7.0/src/alcotest-lwt/alcotest_lwt_intf.ml000066400000000000000000000007671437617477400226620ustar00rootroot00000000000000module type V1 = sig include Alcotest_engine.V1.Cli.S with type return = unit Lwt.t val test_case : string -> Alcotest.speed_level -> (Lwt_switch.t -> 'a -> unit Lwt.t) -> 'a test_case val test_case_sync : string -> Alcotest.speed_level -> ('a -> unit) -> 'a test_case end module type Alcotest_lwt = sig include V1 (** {1 Versioned APIs} *) module V1 : V1 (** An alias of the above API that provides a stability guarantees over major version changes. *) end alcotest-1.7.0/src/alcotest-lwt/dune000066400000000000000000000003121437617477400174440ustar00rootroot00000000000000(library (name alcotest_lwt) (public_name alcotest-lwt) (libraries logs lwt fmt alcotest.engine alcotest (re_export lwt.unix) ;; Unused dependency added for convenience to consumers )) alcotest-1.7.0/src/alcotest-mirage/000077500000000000000000000000001437617477400172305ustar00rootroot00000000000000alcotest-1.7.0/src/alcotest-mirage/alcotest_mirage.ml000066400000000000000000000025521437617477400227300ustar00rootroot00000000000000module Make (C : Mirage_clock.MCLOCK) = struct module Platform (M : Alcotest_engine.Monad.S) = struct let time () = Duration.to_f @@ C.elapsed_ns () let getcwd () = "" let stdout_isatty () = true let stdout_columns () = None let setup_std_outputs ?style_renderer:_ ?utf_8:_ () = () (* Pre-4.07 doesn't support empty variant types. *) type file_descriptor = { empty : 'a. 'a } let log_trap_supported = false let prepare_log_trap ~root:_ = assert false let file_exists _ = assert false let open_write_only _ = assert false let close = function (fd : file_descriptor) -> fd.empty let with_redirect = function (fd : file_descriptor) -> fd.empty let home_directory () = Error (`Msg "Home directory not available for the MirageOS platform") end module Tester = Alcotest_engine.V1.Cli.Make (Platform) (Lwt) include Tester let test_case_sync n s f = test_case n s (fun x -> Lwt.return (f x)) let run_test fn args = let async_ex, async_waker = Lwt.wait () in let handle_exn ex = Logs.debug (fun f -> f "Uncaught async exception: %a" Fmt.exn ex); if Lwt.state async_ex = Lwt.Sleep then Lwt.wakeup_exn async_waker ex in Lwt.async_exception_hook := handle_exn; Lwt_switch.with_switch (fun sw -> Lwt.pick [ fn sw args; async_ex ]) let test_case n s f = test_case n s (run_test f) end alcotest-1.7.0/src/alcotest-mirage/alcotest_mirage.mli000066400000000000000000000027211437617477400230770ustar00rootroot00000000000000(* * Copyright (c) 2017 Thomas Gazagnaire * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) (** [Alcotest_mirage] enables testing functions which return an Lwt promise. {!Make.run} returns a promise that runs the tests when scheduled, catching any asynchronous exceptions thrown by the tests. Please note that this backend does not support redirection of standard streams into files (MirageOS does not have a file system). It writes all test output to the console. *) module Make (_ : Mirage_clock.MCLOCK) : sig include Alcotest_engine.V1.Cli.S with type return = unit Lwt.t val test_case : string -> speed_level -> (Lwt_switch.t -> 'a -> unit Lwt.t) -> 'a test_case val test_case_sync : string -> speed_level -> ('a -> unit) -> 'a test_case end alcotest-1.7.0/src/alcotest-mirage/dune000066400000000000000000000002011437617477400200770ustar00rootroot00000000000000(library (public_name alcotest-mirage) (name alcotest_mirage) (libraries alcotest.engine duration mirage-clock logs fmt lwt)) alcotest-1.7.0/src/alcotest-stdlib-ext/000077500000000000000000000000001437617477400200435ustar00rootroot00000000000000alcotest-1.7.0/src/alcotest-stdlib-ext/alcotest_stdlib_ext.ml000066400000000000000000000046731437617477400244460ustar00rootroot00000000000000let ( >> ) f g x = x |> f |> g module Fun = struct let id x = x end module Int = struct module Set = Set.Make (struct type t = int let compare = (compare : int -> int -> int) end) end module String = struct include Astring.String let length_utf8 = Uutf.String.fold_utf_8 (fun count _ _ -> count + 1) 0 let prefix_utf8 uchars string = let exception Found_new_length of int in try let (_ : int) = Uutf.String.fold_utf_8 (fun count start_pos _ -> if count = uchars then raise (Found_new_length start_pos) else count + 1) 0 string in string with Found_new_length l -> String.sub string 0 l end module List = struct include List type 'a t = 'a list let rev_head n l = let rec aux acc n l = match l with | x :: xs -> if n > 0 then (aux [@tailcall]) (x :: acc) (n - 1) xs else acc | [] -> acc in aux [] n l let concat_map f l = let rec aux f acc = function | [] -> rev acc | x :: l -> let xs = f x in (aux [@tailcall]) f (rev_append xs acc) l in aux f [] l let filter_map f l = let rec inner acc = function | [] -> rev acc | x :: xs -> ( match f x with | None -> (inner [@tailcall]) acc xs | Some y -> (inner [@tailcall]) (y :: acc) xs) in inner [] l let lift_result l = List.fold_right (fun a b -> match (a, b) with | Ok o, Ok acc -> Ok (o :: acc) | Ok _, Error e -> Error e | Error e, Error acc -> Error (e :: acc) | Error e, Ok _ -> Error [ e ]) l (Ok []) let init n f = let rec aux acc i = if i >= n then rev acc else aux (f i :: acc) (i + 1) in aux [] 0 end module Result = struct let map f = function Ok x -> Ok (f x) | Error e -> Error e end module Option = struct let is_some = function Some _ -> true | None -> false let map f = function Some x -> Some (f x) | None -> None let get_exn = function | Some x -> x | None -> invalid_arg "Option.get_exn: None" let value ~default = function None -> default | Some x -> x let ( || ) a b = match (a, b) with | None, None -> None | (Some _ as x), _ | None, (Some _ as x) -> x end module Cmdliner_syntax = struct open Cmdliner let ( let+ ) t f = Term.(const f $ t) let ( and+ ) a b = Term.(const (fun x y -> (x, y)) $ a $ b) let ( >>| ) x f = Term.(app (const f) x) end alcotest-1.7.0/src/alcotest-stdlib-ext/alcotest_stdlib_ext.mli000066400000000000000000000030601437617477400246040ustar00rootroot00000000000000(** This module exists for internal Alcotest use only. It provides no stability guarantee. *) val ( >> ) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c module Fun : sig val id : 'a -> 'a end module Int : sig module Set : Set.S with type elt = int end module String : sig include module type of Astring.String val length_utf8 : string -> int (** Get the length of a string in UTF-8 characters and malformed segments. *) val prefix_utf8 : int -> string -> string (** [prefix_utf8 n s] is the prefix of [s] containing [n] UTF-8 characters (or [s] if it contains fewer than [n] UTF-8 characters). *) end module List : sig include module type of List type 'a t = 'a list val rev_head : int -> 'a list -> 'a list (** Reverse a list, taking at most the first n elements of the original list. *) val concat_map : ('a -> 'b list) -> 'a list -> 'b list val filter_map : ('a -> 'b option) -> 'a list -> 'b list val lift_result : ('a, 'b) result t -> ('a t, 'b t) result val init : int -> (int -> 'a) -> 'a list end module Result : sig val map : ('a -> 'b) -> ('a, 'e) result -> ('b, 'e) result end module Option : sig val map : ('a -> 'b) -> 'a option -> 'b option val is_some : _ option -> bool val get_exn : 'a option -> 'a val value : default:'a -> 'a option -> 'a val ( || ) : 'a option -> 'a option -> 'a option end module Cmdliner_syntax : sig open Cmdliner val ( let+ ) : 'a Term.t -> ('a -> 'b) -> 'b Term.t val ( and+ ) : 'a Term.t -> 'b Term.t -> ('a * 'b) Term.t val ( >>| ) : 'a Term.t -> ('a -> 'b) -> 'b Term.t end alcotest-1.7.0/src/alcotest-stdlib-ext/dune000066400000000000000000000002101437617477400207120ustar00rootroot00000000000000(library (name alcotest_stdlib_ext) (public_name alcotest.stdlib_ext) (libraries astring cmdliner uutf) (preprocess future_syntax)) alcotest-1.7.0/src/alcotest/000077500000000000000000000000001437617477400157665ustar00rootroot00000000000000alcotest-1.7.0/src/alcotest/alcotest.ml000066400000000000000000000100151437617477400201330ustar00rootroot00000000000000module Unix_platform (M : Alcotest_engine.Monad.S) = struct module M = Alcotest_engine.Monad.Extend (M) module Unix = struct open Astring include Unix let mkdir_p path mode = let is_win_drive_letter x = String.length x = 2 && x.[1] = ':' && Char.Ascii.is_letter x.[0] in let sep = Filename.dir_sep in let rec mk parent = function | [] -> () | name :: names -> let path = parent ^ sep ^ name in (try Unix.mkdir path mode with Unix.Unix_error (Unix.EEXIST, _, _) -> if Sys.is_directory path then () (* the directory exists *) else Fmt.str "mkdir: %s: is a file" path |> failwith); mk path names in match String.cuts ~empty:true ~sep path with | "" :: xs -> mk sep xs (* check for Windows drive letter *) | dl :: xs when is_win_drive_letter dl -> mk dl xs | xs -> mk "." xs end open M.Syntax let time = Unix.gettimeofday let getcwd = Sys.getcwd let unlink_if_exists file = let rec inner ~retries = try Unix.unlink file with | Unix.Unix_error (Unix.ENOENT, _, _) -> () | Unix.Unix_error (Unix.EINTR, _, _) -> if retries > 5 then Fmt.failwith "Failed %d times to unlink file %s (Unix.EINTR)." retries file else inner ~retries:(retries + 1) in inner ~retries:0 let symlink ~to_dir ~target ~link_name = let rec inner ~retries = try Unix.symlink ~to_dir target link_name with Unix.Unix_error (Unix.EEXIST, _, _) -> if retries > 5 then Fmt.failwith "Failed %d times to create symlink %s (Unix.EEXIST)" retries target else ( unlink_if_exists link_name; inner ~retries:(retries + 1)) in inner ~retries:0 let prepare_log_trap ~root ~uuid ~name = let dir = Filename.concat root uuid in if not (Sys.file_exists dir) then ( Unix.mkdir_p dir 0o770; if (Sys.unix || Sys.cygwin) && Unix.has_symlink () then ( let this_exe = Filename.concat root name and latest = Filename.concat root "latest" in unlink_if_exists this_exe; unlink_if_exists latest; symlink ~to_dir:true ~target:dir ~link_name:this_exe; symlink ~to_dir:true ~target:dir ~link_name:latest)) else if not (Sys.is_directory dir) then Fmt.failwith "exists but is not a directory: %S" dir let stdout_isatty () = Unix.(isatty stdout) let stdout_columns () = if Sys.win32 then None else match Terminal.get_dimensions () with | Some { columns; _ } -> Some columns | None -> None external before_test : output:out_channel -> stdout:out_channel -> stderr:out_channel -> unit = "alcotest_before_test" external after_test : stdout:out_channel -> stderr:out_channel -> unit = "alcotest_after_test" type file_descriptor = out_channel let log_trap_supported = true let file_exists = Sys.file_exists let open_write_only = open_out let close = close_out let with_redirect fd_file fn = let* () = M.return () in Fmt.(flush stdout) (); Fmt.(flush stderr) (); before_test ~output:fd_file ~stdout ~stderr; let+ r = try fn () >|= fun o -> `Ok o with e -> M.return @@ `Error e in Fmt.(flush stdout ()); Fmt.(flush stderr ()); after_test ~stdout ~stderr; match r with `Ok x -> x | `Error e -> raise e let setup_std_outputs = Fmt_tty.setup_std_outputs (* Implementation similar to that of [Bos.Os.Dir]. *) let home_directory () = let env_var_fallback () = try Ok (Sys.getenv "HOME") with Not_found -> Error (`Msg "HOME environment variable is undefined") in if Sys.win32 then env_var_fallback () else try let uid = Unix.getuid () in Ok (Unix.getpwuid uid).Unix.pw_dir with Not_found -> env_var_fallback () end module V1 = struct include Alcotest_engine.V1.Test module T = Alcotest_engine.V1.Cli.Make (Unix_platform) (Alcotest_engine.Monad.Identity) include T end include V1 alcotest-1.7.0/src/alcotest/alcotest.mli000066400000000000000000000040041437617477400203050ustar00rootroot00000000000000(* * Copyright (c) 2013 Thomas Gazagnaire * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) (** A lightweight and colourful test framework. [Alcotest] provides a simple interface to perform unit tests. It exposes a simple {{!TESTABLE} TESTABLE} module type, a {{!check} check function} to assert test predicates and a {{!run} run} function to perform a list of [unit -> unit] test callbacks. From these descriptions, [Alcotest] builds a quiet and colorful output where only faulty runs are fully displayed at the end of the run (with the full logs ready to inspect). {e Release %%VERSION%%} *) include Alcotest_engine.V1.Cli.S with type return = unit include module type of Alcotest_engine.V1.Test (** @inline *) (** {1 Versioned APIs} *) (** An alias of the above API that provides a stability guarantees over major version changes. *) module V1 : sig include Alcotest_engine.V1.Cli.S with type return = unit include module type of Alcotest_engine.V1.Test (** @inline *) end (** {1 Unix-specific engine constructors} The [Alcotest_engine] package provides the most general form of the Alcotest API, parameterised over the thread implementation and the platform. This package provides the [Unix] platform implementation. *) module Unix_platform : Alcotest_engine.Platform.MAKER alcotest-1.7.0/src/alcotest/alcotest_stubs.c000066400000000000000000000064731437617477400212020ustar00rootroot00000000000000#include #include #include #include #ifndef _MSC_VER #include #endif #if OCAML_VERSION < 41200 #define Val_none Val_int(0) #define Tag_some 0 static value caml_alloc_some(value v) { CAMLparam1(v); value some = caml_alloc_small(1, Tag_some); Field(some, 0) = v; CAMLreturn(some); } #endif // Detect platform #if defined(_WIN32) #define OCAML_ALCOTEST_WINDOWS #elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) #if defined(_POSIX_VERSION) #define OCAML_ALCOTEST_POSIX #endif #endif // Windows support #if defined(OCAML_ALCOTEST_WINDOWS) #define WIN32_LEAN_AND_MEAN #define VC_EXTRALEAN #include CAMLprim value ocaml_alcotest_get_terminal_dimensions(value unit) { CAMLparam1(unit); CAMLlocal2(result, pair); CONSOLE_SCREEN_BUFFER_INFO csbi; int success = GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); if (success) { pair = caml_alloc_tuple(2); Store_field(pair, 0, Val_int((int)(csbi.dwSize.Y))); Store_field(pair, 1, Val_int((int)(csbi.dwSize.X))); result = caml_alloc_some(pair); } else { result = Val_none; } CAMLreturn(result); } // POSIX support #elif defined(OCAML_ALCOTEST_POSIX) #include CAMLprim value ocaml_alcotest_get_terminal_dimensions(value unit) { CAMLparam1(unit); CAMLlocal2(result, pair); struct winsize ws; int z = ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); if (z == 0) { pair = caml_alloc_tuple(2); Store_field(pair, 0, Val_int(ws.ws_row)); Store_field(pair, 1, Val_int(ws.ws_col)); result = caml_alloc_some(pair); } else { result = Val_none; } CAMLreturn(result); } // Unsupported platform #else CAMLprim value ocaml_alcotest_get_terminal_dimensions(value unit) { CAMLparam1(unit); CAMLlocal1(result); result = Val_none; CAMLreturn(result); } #endif /* duplicated from caml/sys.h and io.c */ CAMLextern value caml_channel_descriptor(value vchannel); #define NO_ARG Val_int(0) CAMLextern void caml_sys_error (value); /* End of code duplication */ static int alcotest_saved_stdout; static int alcotest_saved_stderr; CAMLprim value alcotest_before_test (value voutput, value vstdout, value vstderr) { int output_fd, stdout_fd, stderr_fd, fd, ret; stdout_fd = Int_val(caml_channel_descriptor(vstdout)); stderr_fd = Int_val(caml_channel_descriptor(vstderr)); output_fd = Int_val(caml_channel_descriptor(voutput)); fd = dup(stdout_fd); if(fd == -1) caml_sys_error(NO_ARG); alcotest_saved_stdout = fd; fd = dup(stderr_fd); if(fd == -1) caml_sys_error(NO_ARG); alcotest_saved_stderr = fd; ret = dup2(output_fd, stdout_fd); if(ret == -1) caml_sys_error(NO_ARG); ret = dup2(output_fd, stderr_fd); if(ret == -1) caml_sys_error(NO_ARG); return Val_unit; } CAMLprim value alcotest_after_test (value vstdout, value vstderr) { int stdout_fd, stderr_fd, ret; stdout_fd = Int_val(caml_channel_descriptor(vstdout)); stderr_fd = Int_val(caml_channel_descriptor(vstderr)); ret = dup2(alcotest_saved_stdout, stdout_fd); if(ret == -1) caml_sys_error(NO_ARG); ret = dup2(alcotest_saved_stderr, stderr_fd); if(ret == -1) caml_sys_error(NO_ARG); ret = close(alcotest_saved_stdout); if(ret == -1) caml_sys_error(NO_ARG); ret = close(alcotest_saved_stderr); if(ret == -1) caml_sys_error(NO_ARG); return Val_unit; } alcotest-1.7.0/src/alcotest/dune000066400000000000000000000003341437617477400166440ustar00rootroot00000000000000(library (public_name alcotest) (libraries alcotest.engine astring fmt fmt.tty unix) (foreign_stubs (language c) (names alcotest_stubs)) (js_of_ocaml (javascript_files runtime.js)) (preprocess future_syntax)) alcotest-1.7.0/src/alcotest/runtime.js000066400000000000000000000021611437617477400200070ustar00rootroot00000000000000//Provides: alcotest_saved_stdout var alcotest_saved_stdout //Provides: alcotest_saved_stderr var alcotest_saved_stderr //Provides: alcotest_before_test //Requires: caml_global_data, caml_ml_channels //Requires: alcotest_saved_stderr, alcotest_saved_stdout function alcotest_before_test (voutput, vstdout, vstderr){ alcotest_saved_stderr = caml_ml_channels[vstderr]; alcotest_saved_stdout = caml_ml_channels[vstdout]; var output = caml_ml_channels[voutput]; caml_ml_channels[vstdout] = output; caml_ml_channels[vstderr] = output; return 0; } //Provides: alcotest_after_test //Requires: caml_global_data, caml_ml_channels //Requires: alcotest_saved_stderr, alcotest_saved_stdout function alcotest_after_test (vstdout, vstderr){ caml_ml_channels[vstdout] = alcotest_saved_stdout; caml_ml_channels[vstderr] = alcotest_saved_stderr; return 0; } //Provides: ocaml_alcotest_get_terminal_dimensions function ocaml_alcotest_get_terminal_dimensions(unit) { var p = joo_global_object.process if(p && p.stdout && p.stdout.columns && p.stdout.rows) { return [0, [0, p.stdout.rows, p.stdout.columns]]; } return 0; } alcotest-1.7.0/src/alcotest/terminal.ml000066400000000000000000000002221437617477400201270ustar00rootroot00000000000000type dimensions = { rows : int; columns : int } external get_dimensions : unit -> dimensions option = "ocaml_alcotest_get_terminal_dimensions" alcotest-1.7.0/src/alcotest/terminal.mli000066400000000000000000000002121437617477400202770ustar00rootroot00000000000000type dimensions = { rows : int; columns : int } (** Get the dimensions of the terminal *) val get_dimensions : unit -> dimensions option alcotest-1.7.0/test/000077500000000000000000000000001437617477400143405ustar00rootroot00000000000000alcotest-1.7.0/test/e2e/000077500000000000000000000000001437617477400150135ustar00rootroot00000000000000alcotest-1.7.0/test/e2e/alcotest-lwt/000077500000000000000000000000001437617477400174355ustar00rootroot00000000000000alcotest-1.7.0/test/e2e/alcotest-lwt/dune000066400000000000000000000000611437617477400203100ustar00rootroot00000000000000(env (_ (env-vars (ALCOTEST_COLOR auto)))) alcotest-1.7.0/test/e2e/alcotest-lwt/failing/000077500000000000000000000000001437617477400210465ustar00rootroot00000000000000alcotest-1.7.0/test/e2e/alcotest-lwt/failing/async_failure.expected000066400000000000000000000023151437617477400254160ustar00rootroot00000000000000Testing `test/e2e/alcotest-lwt/failing/async_failure.ml'. This run has ID `'. > [FAIL] all 0 one. [OK] all 1 two. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] all 0 one. │ └──────────────────────────────────────────────────────────────────────────────┘ freeing all resources [failure] All is broken Logs saved to `/_build/_tests//all.000.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 2 tests run. alcotest-1.7.0/test/e2e/alcotest-lwt/failing/async_failure.ml000066400000000000000000000007121437617477400242240ustar00rootroot00000000000000let free () = print_endline "freeing all resources"; Lwt.return () let test_lwt switch () = Lwt_switch.add_hook (Some switch) free; Lwt.async (fun () -> failwith "All is broken"); Lwt_unix.sleep 10. let () = let open Alcotest_lwt in Lwt_main.run @@ run __FILE__ [ ( "all", [ test_case "one" `Quick test_lwt; test_case "two" `Quick (fun _ () -> Lwt.return_unit); ] ); ] alcotest-1.7.0/test/e2e/alcotest-lwt/failing/dune000066400000000000000000000005601437617477400217250ustar00rootroot00000000000000(env (_ (env-vars (ALCOTEST_COLOR auto)))) (include dune.inc) (rule (targets dune.gen) (deps (source_tree .)) (action (with-stdout-to %{targets} (run ../../gen_dune_rules.exe --expect-failure --package "alcotest-lwt" --libraries "alcotest-lwt lwt lwt.unix")))) (rule (alias runtest) (action (diff dune.inc dune.gen))) alcotest-1.7.0/test/e2e/alcotest-lwt/failing/dune.inc000066400000000000000000000017521437617477400225010ustar00rootroot00000000000000(executables (names async_failure fail_with ) (libraries alcotest alcotest.stdlib_ext alcotest-lwt lwt lwt.unix) (modes exe) (modules async_failure fail_with ) ) (rule (target async_failure.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:async_failure.exe}))))) (rule (target async_failure.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:async_failure.actual})))) (rule (alias runtest) (package alcotest-lwt) (action (diff async_failure.expected async_failure.processed))) (rule (target fail_with.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:fail_with.exe}))))) (rule (target fail_with.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:fail_with.actual})))) (rule (alias runtest) (package alcotest-lwt) (action (diff fail_with.expected fail_with.processed))) alcotest-1.7.0/test/e2e/alcotest-lwt/failing/fail_with.expected000066400000000000000000000022171437617477400245410ustar00rootroot00000000000000Testing `test/e2e/alcotest-lwt/failing/fail_with.ml'. This run has ID `'. > [FAIL] all 0 one. [OK] all 1 two. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] all 0 one. │ └──────────────────────────────────────────────────────────────────────────────┘ [failure] should fail Logs saved to `/_build/_tests//all.000.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 2 tests run. alcotest-1.7.0/test/e2e/alcotest-lwt/failing/fail_with.ml000066400000000000000000000005061437617477400233470ustar00rootroot00000000000000let test_lwt _switch () = Lwt.fail_with "should fail" let () = let open Alcotest_lwt in Lwt_main.run @@ run ~record_backtrace:false __FILE__ [ ( "all", [ test_case "one" `Quick test_lwt; test_case "two" `Quick (fun _ () -> Lwt.return_unit); ] ); ] alcotest-1.7.0/test/e2e/alcotest-lwt/passing/000077500000000000000000000000001437617477400211015ustar00rootroot00000000000000alcotest-1.7.0/test/e2e/alcotest-lwt/passing/cli_options.expected000066400000000000000000000002271437617477400251470ustar00rootroot00000000000000Testing `Results should be in JSON, since --json is passed'. This run has ID `'. { "success": 1, "failures": 0, "time": } alcotest-1.7.0/test/e2e/alcotest-lwt/passing/cli_options.ml000066400000000000000000000003001437617477400237460ustar00rootroot00000000000000let () = let open Alcotest_lwt in Lwt_main.run @@ run "Results should be in JSON, since --json is passed" [ ("test", [ test_case "alpha" `Quick (fun _ () -> Lwt.return_unit) ]) ] alcotest-1.7.0/test/e2e/alcotest-lwt/passing/cli_options.opts000066400000000000000000000000071437617477400243270ustar00rootroot00000000000000--json alcotest-1.7.0/test/e2e/alcotest-lwt/passing/dune000066400000000000000000000005331437617477400217600ustar00rootroot00000000000000(env (_ (env-vars (ALCOTEST_COLOR auto)))) (include dune.inc) (rule (targets dune.gen) (deps (source_tree .)) (action (with-stdout-to %{targets} (run ../../gen_dune_rules.exe --package "alcotest-lwt" --libraries "alcotest-lwt lwt lwt.unix")))) (rule (alias runtest) (action (diff dune.inc dune.gen))) alcotest-1.7.0/test/e2e/alcotest-lwt/passing/dune.inc000066400000000000000000000010661437617477400225320ustar00rootroot00000000000000(executables (names cli_options ) (libraries alcotest alcotest.stdlib_ext alcotest-lwt lwt lwt.unix) (modes exe) (modules cli_options ) ) (rule (target cli_options.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:cli_options.exe} --json))))) (rule (target cli_options.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:cli_options.actual})))) (rule (alias runtest) (package alcotest-lwt) (action (diff cli_options.expected cli_options.processed))) alcotest-1.7.0/test/e2e/alcotest-mirage/000077500000000000000000000000001437617477400200735ustar00rootroot00000000000000alcotest-1.7.0/test/e2e/alcotest-mirage/config.ml000066400000000000000000000002651437617477400216750ustar00rootroot00000000000000open Mirage let main = foreign ~packages:[ package "alcotest-mirage" ] "Unikernel.Main" (mclock @-> job) let () = register "alcotest" [ main $ default_monotonic_clock ] alcotest-1.7.0/test/e2e/alcotest-mirage/unikernel.ml000066400000000000000000000004511437617477400224210ustar00rootroot00000000000000module Main (C : Mirage_clock.MCLOCK) = struct module Alcotest = Alcotest_mirage.Make (C) let start () = let open Alcotest in let id () = () in run "suite-name" [ ( "test", [ test_case_sync "A test case for alcotest with mirage" `Quick id ] ); ] end alcotest-1.7.0/test/e2e/alcotest/000077500000000000000000000000001437617477400166315ustar00rootroot00000000000000alcotest-1.7.0/test/e2e/alcotest/failing/000077500000000000000000000000001437617477400202425ustar00rootroot00000000000000alcotest-1.7.0/test/e2e/alcotest/failing/bail-js.expected000066400000000000000000000022721437617477400233110ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/bail.ml'. This run has ID `'. [OK] passing 0 a. > [FAIL] failing 0 b. ... with 1 subsequent test skipped. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] failing 0 b. │ └──────────────────────────────────────────────────────────────────────────────┘ [failure] Expected failure Logs saved to `/_build/_tests//failing.000.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 2 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/bail.expected000066400000000000000000000023341437617477400226760ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/bail.ml'. This run has ID `'. [OK] passing 0 a. > [FAIL] failing 0 b. ... with 1 subsequent test skipped. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] failing 0 b. │ └──────────────────────────────────────────────────────────────────────────────┘ [failure] Expected failure Logs saved to `/_build/_tests//failing.000.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 2 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/bail.ml000066400000000000000000000004611437617477400215040ustar00rootroot00000000000000let () = let tc name f = Alcotest.test_case name `Quick f in Alcotest.run ~bail:true __FILE__ [ ("passing", [ tc "a" (fun () -> ()) ]); ( "failing", [ tc "b" (fun () -> failwith "Expected failure"); tc "not_run" (fun () -> assert false); ] ); ] alcotest-1.7.0/test/e2e/alcotest/failing/check_basic-js.expected000066400000000000000000000317431437617477400246250ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/check_basic.ml'. This run has ID `'. ASSERT bool [FAIL] different basic 0 bool. ASSERT int [FAIL] different basic 1 int. ASSERT int32 [FAIL] different basic 2 int32. ASSERT int64 [FAIL] different basic 3 int64. ASSERT float [FAIL] different basic 4 float. ASSERT char [FAIL] different basic 5 char. ASSERT string [FAIL] different basic 6 string. ASSERT bytes [FAIL] different basic 7 bytes. ASSERT list [FAIL] different composite 0 list. ASSERT array [FAIL] different composite 1 array. ASSERT option some [FAIL] different composite 2 option some. ASSERT result [FAIL] different composite 3 result. ASSERT pair [FAIL] different composite 4 pair. ASSERT triple [FAIL] different composite 5 triple. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 0 bool. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL bool Expected: `true' Received: `false' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 1 int. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL int Expected: `1' Received: `2' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 2 int32. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL int32 Expected: `2147483647' Received: `-2147483648' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 3 int64. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL int64 Expected: `9223372036854775807' Received: `-9223372036854775808' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 4 float. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL float Expected: `1' Received: `2' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 5 char. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL char Expected: `'a'' Received: `'b'' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 6 string. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL string Expected: `"Lorem ipsum"' Received: `"dolor sit amet."' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 7 bytes. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL bytes Expected: `"\001\002\003"' Received: `"\001\000\003"' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different composite 0 list. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL list Expected: `['a'; 'b']' Received: `['a'; 'c']' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different composite 1 array. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL array Expected: `[|0; 9223372036854775807|]' Received: `[|0; 1; 9223372036854775807|]' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different composite 2 option some. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL option some Expected: `Some 1' Received: `Some 2' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different composite 3 result. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL result Expected: `Ok 1' Received: `Error ()' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different composite 4 pair. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL pair Expected: `(1, 'a')' Received: `(1, 'b')' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different composite 5 triple. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL triple Expected: `(1, true, "a")' Received: `(1, false, "a")' ────────────────────────────────────────────────────────────────────────────── 14 failures! in s. 14 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/check_basic.expected000066400000000000000000000322471437617477400242130ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/check_basic.ml'. This run has ID `'. ASSERT bool [FAIL] different basic 0 bool. ASSERT int [FAIL] different basic 1 int. ASSERT int32 [FAIL] different basic 2 int32. ASSERT int64 [FAIL] different basic 3 int64. ASSERT float [FAIL] different basic 4 float. ASSERT char [FAIL] different basic 5 char. ASSERT string [FAIL] different basic 6 string. ASSERT bytes [FAIL] different basic 7 bytes. ASSERT list [FAIL] different composite 0 list. ASSERT array [FAIL] different composite 1 array. ASSERT option some [FAIL] different composite 2 option some. ASSERT result [FAIL] different composite 3 result. ASSERT pair [FAIL] different composite 4 pair. ASSERT triple [FAIL] different composite 5 triple. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 0 bool. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL bool Expected: `true' Received: `false' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 1 int. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL int Expected: `1' Received: `2' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 2 int32. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL int32 Expected: `2147483647' Received: `-2147483648' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 3 int64. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL int64 Expected: `9223372036854775807' Received: `-9223372036854775808' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 4 float. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL float Expected: `1' Received: `2' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 5 char. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL char Expected: `'a'' Received: `'b'' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 6 string. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL string Expected: `"Lorem ipsum"' Received: `"dolor sit amet."' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different basic 7 bytes. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL bytes Expected: `"\001\002\003"' Received: `"\001\000\003"' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different composite 0 list. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL list Expected: `['a'; 'b']' Received: `['a'; 'c']' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different composite 1 array. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL array Expected: `[|0; 9223372036854775807|]' Received: `[|0; 1; 9223372036854775807|]' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different composite 2 option some. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL option some Expected: `Some 1' Received: `Some 2' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different composite 3 result. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL result Expected: `Ok 1' Received: `Error ()' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different composite 4 pair. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL pair Expected: `(1, 'a')' Received: `(1, 'b')' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] different composite 5 triple. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL triple Expected: `(1, true, "a")' Received: `(1, false, "a")' ────────────────────────────────────────────────────────────────────────────── 14 failures! in s. 14 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/check_basic.ml000066400000000000000000000024071437617477400230150ustar00rootroot00000000000000(* Check that v of type [typ] matches with itself *) let id_case typ typ_str v1 v2 = Alcotest.test_case typ_str `Quick (fun () -> Alcotest.check typ typ_str v1 v2) let () = let open Alcotest in run ~verbose:true __FILE__ [ ( "different basic", [ id_case bool "bool" true false; id_case int "int" 1 2; id_case int32 "int32" Int32.max_int Int32.min_int; id_case int64 "int64" Int64.max_int Int64.min_int; id_case (float 0.0) "float" 1.0 2.0; id_case char "char" 'a' 'b'; id_case string "string" "Lorem ipsum" "dolor sit amet."; id_case bytes "bytes" (Bytes.of_string "\x01\x02\x03") (Bytes.of_string "\x01\x00\x03"); ] ); ( "different composite", [ id_case (list char) "list" [ 'a'; 'b' ] [ 'a'; 'c' ]; id_case (array int64) "array" Int64.[| zero; max_int |] Int64.[| zero; one; max_int |]; id_case (option int) "option some" (Some 1) (Some 2); id_case (result int unit) "result" (Ok 1) (Error ()); id_case (pair int char) "pair" (1, 'a') (1, 'b'); id_case (triple int bool string) "triple" (1, true, "a") (1, false, "a"); ] ); ] alcotest-1.7.0/test/e2e/alcotest/failing/check_located-js.expected000066400000000000000000000137441437617477400251600ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/check_located.ml'. This run has ID `'. ASSERT Expected failure [FAIL] check 0 here. ASSERT Expected failure [FAIL] check 1 pos. ASSERT Expected failure [FAIL] check_raises 0 here. ASSERT Expected failure [FAIL] check_raises 1 pos. ASSERT Expected failure [FAIL] fail 0 here. ASSERT Expected failure [FAIL] fail 1 pos. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] check 0 here. │ └──────────────────────────────────────────────────────────────────────────────┘ File "test/e2e/alcotest/failing/check_located.ml", line 2, character 30: FAIL Expected failure Expected: `true' Received: `false' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] check 1 pos. │ └──────────────────────────────────────────────────────────────────────────────┘ File "test/e2e/alcotest/failing/check_located.ml", line 4, character 10: FAIL Expected failure Expected: `true' Received: `false' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] check_raises 0 here. │ └──────────────────────────────────────────────────────────────────────────────┘ File "test/e2e/alcotest/failing/check_located.ml", line 2, character 30: FAIL Expected failure: expecting Failure(""), got nothing. ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] check_raises 1 pos. │ └──────────────────────────────────────────────────────────────────────────────┘ File "test/e2e/alcotest/failing/check_located.ml", line 4, character 10: FAIL Expected failure: expecting Failure(""), got nothing. ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] fail 0 here. │ └──────────────────────────────────────────────────────────────────────────────┘ File "test/e2e/alcotest/failing/check_located.ml", line 2, character 30: FAIL Expected failure ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] fail 1 pos. │ └──────────────────────────────────────────────────────────────────────────────┘ File "test/e2e/alcotest/failing/check_located.ml", line 4, character 10: FAIL Expected failure ────────────────────────────────────────────────────────────────────────────── 6 failures! in s. 6 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/check_located.expected000066400000000000000000000140701437617477400245370ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/check_located.ml'. This run has ID `'. ASSERT Expected failure [FAIL] check 0 here. ASSERT Expected failure [FAIL] check 1 pos. ASSERT Expected failure [FAIL] check_raises 0 here. ASSERT Expected failure [FAIL] check_raises 1 pos. ASSERT Expected failure [FAIL] fail 0 here. ASSERT Expected failure [FAIL] fail 1 pos. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] check 0 here. │ └──────────────────────────────────────────────────────────────────────────────┘ File "test/e2e/alcotest/failing/check_located.ml", line 2, character 30: FAIL Expected failure Expected: `true' Received: `false' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] check 1 pos. │ └──────────────────────────────────────────────────────────────────────────────┘ File "test/e2e/alcotest/failing/check_located.ml", line 4, character 10: FAIL Expected failure Expected: `true' Received: `false' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] check_raises 0 here. │ └──────────────────────────────────────────────────────────────────────────────┘ File "test/e2e/alcotest/failing/check_located.ml", line 2, character 30: FAIL Expected failure: expecting Failure(""), got nothing. ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] check_raises 1 pos. │ └──────────────────────────────────────────────────────────────────────────────┘ File "test/e2e/alcotest/failing/check_located.ml", line 4, character 10: FAIL Expected failure: expecting Failure(""), got nothing. ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] fail 0 here. │ └──────────────────────────────────────────────────────────────────────────────┘ File "test/e2e/alcotest/failing/check_located.ml", line 2, character 30: FAIL Expected failure ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] fail 1 pos. │ └──────────────────────────────────────────────────────────────────────────────┘ File "test/e2e/alcotest/failing/check_located.ml", line 4, character 10: FAIL Expected failure ────────────────────────────────────────────────────────────────────────────── 6 failures! in s. 6 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/check_located.ml000066400000000000000000000014461437617477400233510ustar00rootroot00000000000000let here : Lexing.position = { pos_fname = __FILE__; pos_lnum = 2; pos_bol = 20; pos_cnum = 50 } let pos = __POS__ let () = let open Alcotest in let msg = "Expected failure" in let tc msg f = Alcotest.test_case msg `Quick f in run ~verbose:true __FILE__ [ ( "check", [ tc "here" (fun () -> check ~here bool msg true false); tc "pos" (fun () -> check ~pos bool msg true false); ] ); ( "check_raises", [ tc "here" (fun () -> check_raises ~here msg (Failure "") (fun () -> ())); tc "pos" (fun () -> check_raises ~pos msg (Failure "") (fun () -> ())); ] ); ( "fail", [ tc "here" (fun () -> fail ~here msg); tc "pos" (fun () -> fail ~pos msg); ] ); ] alcotest-1.7.0/test/e2e/alcotest/failing/check_long-js.expected000066400000000000000000000172441437617477400245030ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/check_long.ml'. This run has ID `'. ASSERT list [FAIL] wrapping 0 list. ASSERT array [FAIL] wrapping 1 array. ASSERT nested options [FAIL] wrapping 2 nested options. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] wrapping 0 list. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL list Expected: `['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'i'; 'j'; 'k'; 'l'; 'm'; 'n'; 'o'; 'p'; 'q'; 'r'; 's'; 't'; 'u'; 'v'; 'w'; 'x'; 'y'; 'z'; 'A']' Received: `['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'i'; 'j'; 'k'; 'l'; 'm'; 'n'; 'o'; 'p'; 'q'; 'r'; 's'; 't'; 'u'; 'v'; 'w'; 'x'; 'y'; 'z'; 'B']' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] wrapping 1 array. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL array Expected: `[|0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28; 29; 30; 31; 32; 33; 34; 35; 36; 37; 38; 39; 40; 41; 42; 43; 44; 45; 46; 47; 48; 49; 50; 51; 52; 53; 54; 55; 56; 57; 58; 59; 60; 61; 62; 63; 64; 65; 66; 67; 68; 69; 70; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; 88; 89; 90; 91; 92; 93; 94; 95; 96; 97; 98; 99; 9223372036854775807|]' Received: `[|0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28; 29; 30; 31; 32; 33; 34; 35; 36; 37; 38; 39; 40; 41; 42; 43; 44; 45; 46; 47; 48; 49; 50; 51; 52; 53; 54; 55; 56; 57; 58; 59; 60; 61; 62; 63; 64; 65; 66; 67; 68; 69; 70; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; 88; 89; 90; 91; 92; 93; 94; 95; 96; 97; 98; 99; -9223372036854775808|]' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] wrapping 2 nested options. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL nested options Expected: `Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some 1' Received: `Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some 2' ────────────────────────────────────────────────────────────────────────────── 3 failures! in s. 3 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/check_long.expected000066400000000000000000000173161437617477400240710ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/check_long.ml'. This run has ID `'. ASSERT list [FAIL] wrapping 0 list. ASSERT array [FAIL] wrapping 1 array. ASSERT nested options [FAIL] wrapping 2 nested options. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] wrapping 0 list. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL list Expected: `['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'i'; 'j'; 'k'; 'l'; 'm'; 'n'; 'o'; 'p'; 'q'; 'r'; 's'; 't'; 'u'; 'v'; 'w'; 'x'; 'y'; 'z'; 'A']' Received: `['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'i'; 'j'; 'k'; 'l'; 'm'; 'n'; 'o'; 'p'; 'q'; 'r'; 's'; 't'; 'u'; 'v'; 'w'; 'x'; 'y'; 'z'; 'B']' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] wrapping 1 array. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL array Expected: `[|0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28; 29; 30; 31; 32; 33; 34; 35; 36; 37; 38; 39; 40; 41; 42; 43; 44; 45; 46; 47; 48; 49; 50; 51; 52; 53; 54; 55; 56; 57; 58; 59; 60; 61; 62; 63; 64; 65; 66; 67; 68; 69; 70; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; 88; 89; 90; 91; 92; 93; 94; 95; 96; 97; 98; 99; 9223372036854775807|]' Received: `[|0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28; 29; 30; 31; 32; 33; 34; 35; 36; 37; 38; 39; 40; 41; 42; 43; 44; 45; 46; 47; 48; 49; 50; 51; 52; 53; 54; 55; 56; 57; 58; 59; 60; 61; 62; 63; 64; 65; 66; 67; 68; 69; 70; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; 88; 89; 90; 91; 92; 93; 94; 95; 96; 97; 98; 99; -9223372036854775808|]' ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] wrapping 2 nested options. │ └──────────────────────────────────────────────────────────────────────────────┘ FAIL nested options Expected: `Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some 1' Received: `Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some Some 2' ────────────────────────────────────────────────────────────────────────────── 3 failures! in s. 3 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/check_long.ml000066400000000000000000000023261437617477400226730ustar00rootroot00000000000000open Alcotest_stdlib_ext (** These tests check the wrapping behaviour of [Alcotest.check]'s emitted diff. *) let id_case typ typ_str v1 v2 = Alcotest.test_case typ_str `Quick (fun () -> Alcotest.check typ typ_str v1 v2) let test_char_list = let alphabet = List.init 26 (fun i -> Char.chr (0x61 + i)) in id_case Alcotest.(list char) "list" (alphabet @ [ 'A' ]) (alphabet @ [ 'B' ]) let test_int64_array = let ns = Array.init 100 Int64.of_int in id_case Alcotest.(array int64) "array" (Array.append ns Int64.[| max_int |]) (Array.append ns Int64.[| min_int |]) type with_testables = E : 'a Alcotest.testable * 'a * 'a -> with_testables (* Take two values and wrap them in [n]-many [Some]s. *) let nested_option n : with_testables -> with_testables = fun t -> let rec inner = function | 0 -> t | n -> let (E (typ', v1', v2')) = inner (n - 1) in E (Alcotest.option typ', Some v1', Some v2') in inner n let test_nested_options = let (E (typ, v1, v2)) = nested_option 35 (E (Alcotest.int, 1, 2)) in id_case typ "nested options" v1 v2 let () = let open Alcotest in run ~verbose:true __FILE__ [ ("wrapping", [ test_char_list; test_int64_array; test_nested_options ]) ] alcotest-1.7.0/test/e2e/alcotest/failing/compact-js.expected000066400000000000000000000021001437617477400240160ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/compact.ml'. This run has ID `'. ..F... ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] alpha 2 3. │ └──────────────────────────────────────────────────────────────────────────────┘ [failure] Error Logs saved to `/_build/_tests//alpha.002.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 6 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/compact.expected000066400000000000000000000021421437617477400234120ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/compact.ml'. This run has ID `'. ..F... ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] alpha 2 3. │ └──────────────────────────────────────────────────────────────────────────────┘ [failure] Error Logs saved to `/_build/_tests//alpha.002.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 6 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/compact.ml000066400000000000000000000005211437617477400222200ustar00rootroot00000000000000let () = let open Alcotest in let passing s = test_case s `Quick (fun () -> ()) in let failing s = test_case s `Quick (fun () -> failwith "Error") in run ~compact:true __FILE__ [ ("alpha", [ passing "1"; passing "2"; failing "3"; passing "4" ]); ("beta", [ passing "1" ]); ("gamma", [ passing "1" ]); ] alcotest-1.7.0/test/e2e/alcotest/failing/dune000066400000000000000000000005771437617477400211310ustar00rootroot00000000000000(env (_ (env-vars (ALCOTEST_COLOR auto)))) (include dune.inc) (rule (targets dune.gen) (deps (source_tree .)) (action (with-stdout-to %{targets} (run ../../gen_dune_rules.exe --package "alcotest" --libraries "alcotest.engine" --expect-failure --js)))) (rule (alias runtest) (package alcotest) (action (diff dune.inc dune.gen))) alcotest-1.7.0/test/e2e/alcotest/failing/dune.inc000066400000000000000000000331561437617477400217000ustar00rootroot00000000000000(executables (names bail check_basic check_located check_long compact duplicate_test_names empty_suite_name exception_in_test filter_all_tests invalid_arg_in_test long_test_case_name outside_runner tail_errors_limit tail_errors_unlimited unknown_option ) (libraries alcotest alcotest.stdlib_ext alcotest.engine) (modes exe js) (modules bail check_basic check_located check_long compact duplicate_test_names empty_suite_name exception_in_test filter_all_tests invalid_arg_in_test long_test_case_name outside_runner tail_errors_limit tail_errors_unlimited unknown_option ) ) (rule (target bail.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:bail.exe}))))) (rule (target bail.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:bail.actual})))) (rule (alias runtest) (package alcotest) (action (diff bail.expected bail.processed))) (rule (target bail-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:bail.bc.js}))))) (rule (target bail-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:bail-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff bail-js.expected bail-js.processed))) (rule (target check_basic.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:check_basic.exe}))))) (rule (target check_basic.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:check_basic.actual})))) (rule (alias runtest) (package alcotest) (action (diff check_basic.expected check_basic.processed))) (rule (target check_basic-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:check_basic.bc.js}))))) (rule (target check_basic-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:check_basic-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff check_basic-js.expected check_basic-js.processed))) (rule (target check_located.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:check_located.exe}))))) (rule (target check_located.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:check_located.actual})))) (rule (alias runtest) (package alcotest) (action (diff check_located.expected check_located.processed))) (rule (target check_located-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:check_located.bc.js}))))) (rule (target check_located-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:check_located-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff check_located-js.expected check_located-js.processed))) (rule (target check_long.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:check_long.exe}))))) (rule (target check_long.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:check_long.actual})))) (rule (alias runtest) (package alcotest) (action (diff check_long.expected check_long.processed))) (rule (target check_long-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:check_long.bc.js}))))) (rule (target check_long-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:check_long-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff check_long-js.expected check_long-js.processed))) (rule (target compact.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:compact.exe}))))) (rule (target compact.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:compact.actual})))) (rule (alias runtest) (package alcotest) (action (diff compact.expected compact.processed))) (rule (target compact-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:compact.bc.js}))))) (rule (target compact-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:compact-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff compact-js.expected compact-js.processed))) (rule (target duplicate_test_names.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:duplicate_test_names.exe}))))) (rule (target duplicate_test_names.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:duplicate_test_names.actual})))) (rule (alias runtest) (package alcotest) (action (diff duplicate_test_names.expected duplicate_test_names.processed))) (rule (target duplicate_test_names-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:duplicate_test_names.bc.js}))))) (rule (target duplicate_test_names-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:duplicate_test_names-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff duplicate_test_names-js.expected duplicate_test_names-js.processed))) (rule (target empty_suite_name.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:empty_suite_name.exe}))))) (rule (target empty_suite_name.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:empty_suite_name.actual})))) (rule (alias runtest) (package alcotest) (action (diff empty_suite_name.expected empty_suite_name.processed))) (rule (target empty_suite_name-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:empty_suite_name.bc.js}))))) (rule (target empty_suite_name-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:empty_suite_name-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff empty_suite_name-js.expected empty_suite_name-js.processed))) (rule (target exception_in_test.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:exception_in_test.exe}))))) (rule (target exception_in_test.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:exception_in_test.actual})))) (rule (alias runtest) (package alcotest) (action (diff exception_in_test.expected exception_in_test.processed))) (rule (target exception_in_test-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:exception_in_test.bc.js}))))) (rule (target exception_in_test-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:exception_in_test-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff exception_in_test-js.expected exception_in_test-js.processed))) (rule (target filter_all_tests.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:filter_all_tests.exe} test bar 1))))) (rule (target filter_all_tests.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:filter_all_tests.actual})))) (rule (alias runtest) (package alcotest) (action (diff filter_all_tests.expected filter_all_tests.processed))) (rule (target filter_all_tests-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:filter_all_tests.bc.js} test bar 1))))) (rule (target filter_all_tests-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:filter_all_tests-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff filter_all_tests-js.expected filter_all_tests-js.processed))) (rule (target invalid_arg_in_test.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:invalid_arg_in_test.exe}))))) (rule (target invalid_arg_in_test.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:invalid_arg_in_test.actual})))) (rule (alias runtest) (package alcotest) (action (diff invalid_arg_in_test.expected invalid_arg_in_test.processed))) (rule (target invalid_arg_in_test-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:invalid_arg_in_test.bc.js}))))) (rule (target invalid_arg_in_test-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:invalid_arg_in_test-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff invalid_arg_in_test-js.expected invalid_arg_in_test-js.processed))) (rule (target long_test_case_name.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:long_test_case_name.exe}))))) (rule (target long_test_case_name.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:long_test_case_name.actual})))) (rule (alias runtest) (package alcotest) (action (diff long_test_case_name.expected long_test_case_name.processed))) (rule (target long_test_case_name-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:long_test_case_name.bc.js}))))) (rule (target long_test_case_name-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:long_test_case_name-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff long_test_case_name-js.expected long_test_case_name-js.processed))) (rule (target outside_runner.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:outside_runner.exe}))))) (rule (target outside_runner.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:outside_runner.actual})))) (rule (alias runtest) (package alcotest) (action (diff outside_runner.expected outside_runner.processed))) (rule (target outside_runner-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:outside_runner.bc.js}))))) (rule (target outside_runner-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:outside_runner-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff outside_runner-js.expected outside_runner-js.processed))) (rule (target tail_errors_limit.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:tail_errors_limit.exe}))))) (rule (target tail_errors_limit.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:tail_errors_limit.actual})))) (rule (alias runtest) (package alcotest) (action (diff tail_errors_limit.expected tail_errors_limit.processed))) (rule (target tail_errors_limit-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:tail_errors_limit.bc.js}))))) (rule (target tail_errors_limit-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:tail_errors_limit-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff tail_errors_limit-js.expected tail_errors_limit-js.processed))) (rule (target tail_errors_unlimited.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:tail_errors_unlimited.exe}))))) (rule (target tail_errors_unlimited.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:tail_errors_unlimited.actual})))) (rule (alias runtest) (package alcotest) (action (diff tail_errors_unlimited.expected tail_errors_unlimited.processed))) (rule (target tail_errors_unlimited-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:tail_errors_unlimited.bc.js}))))) (rule (target tail_errors_unlimited-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:tail_errors_unlimited-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff tail_errors_unlimited-js.expected tail_errors_unlimited-js.processed))) (rule (target unknown_option.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run %{dep:unknown_option.exe} --dry-runn))))) (rule (target unknown_option.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:unknown_option.actual})))) (rule (alias runtest) (package alcotest) (action (diff unknown_option.expected unknown_option.processed))) (rule (target unknown_option-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 1 2 124 125) (run node %{dep:unknown_option.bc.js} --dry-runn))))) (rule (target unknown_option-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:unknown_option-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff unknown_option-js.expected unknown_option-js.processed))) alcotest-1.7.0/test/e2e/alcotest/failing/duplicate_test_names-js.expected000066400000000000000000000000441437617477400265710ustar00rootroot00000000000000ERROR: Duplicate test path: `duped' alcotest-1.7.0/test/e2e/alcotest/failing/duplicate_test_names.expected000066400000000000000000000000441437617477400261570ustar00rootroot00000000000000ERROR: Duplicate test path: `duped' alcotest-1.7.0/test/e2e/alcotest/failing/duplicate_test_names.ml000066400000000000000000000004121437617477400247650ustar00rootroot00000000000000(** Ensure that suites with duplicate test names are rejected. *) let () = Alcotest.run __FILE__ [ ("duped", [ Alcotest.test_case "1" `Quick (fun () -> assert false) ]); ("duped", [ Alcotest.test_case "2" `Quick (fun () -> assert false) ]); ] alcotest-1.7.0/test/e2e/alcotest/failing/empty_suite_name-js.expected000066400000000000000000000001231437617477400257420ustar00rootroot00000000000000ERROR: Suite name cannot cannot be empty. Please pass a non-empty string to `run`. alcotest-1.7.0/test/e2e/alcotest/failing/empty_suite_name.expected000066400000000000000000000001231437617477400253300ustar00rootroot00000000000000ERROR: Suite name cannot cannot be empty. Please pass a non-empty string to `run`. alcotest-1.7.0/test/e2e/alcotest/failing/empty_suite_name.ml000066400000000000000000000001511437617477400241400ustar00rootroot00000000000000let () = Alcotest.run "" [ ("alpha", [ Alcotest.test_case "1" `Quick (fun () -> assert false) ]) ] alcotest-1.7.0/test/e2e/alcotest/failing/exception_in_test-js.expected000066400000000000000000000023701437617477400261240ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/exception_in_test.ml'. This run has ID `'. [OK] test-a 0 Passing. > [FAIL] test-a 1 Failing. [OK] test-b 0 Another pass. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] test-a 1 Failing. │ └──────────────────────────────────────────────────────────────────────────────┘ [exception] Dune__exe__Exception_in_test.Foo("message") Logs saved to `/_build/_tests//test-a.001.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 3 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/exception_in_test.expected000066400000000000000000000024361437617477400255150ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/exception_in_test.ml'. This run has ID `'. [OK] test-a 0 Passing. > [FAIL] test-a 1 Failing. [OK] test-b 0 Another pass. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] test-a 1 Failing. │ └──────────────────────────────────────────────────────────────────────────────┘ [exception] Dune__exe__Exception_in_test.Foo("message") Logs saved to `/_build/_tests//test-a.001.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 3 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/exception_in_test.ml000066400000000000000000000005001437617477400243120ustar00rootroot00000000000000exception Foo of string let () = let open Alcotest in run __FILE__ [ ( "test-a", [ test_case "Passing" `Quick (fun () -> ()); test_case "Failing" `Quick (fun () -> raise (Foo "message")); ] ); ("test-b", [ test_case "Another pass" `Quick (fun () -> ()) ]); ] alcotest-1.7.0/test/e2e/alcotest/failing/filter_all_tests-js.expected000066400000000000000000000002221437617477400257320ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/filter_all_tests.ml'. Invalid request (no tests to run, filter skipped everything)! This run has ID `'. alcotest-1.7.0/test/e2e/alcotest/failing/filter_all_tests.expected000066400000000000000000000002221437617477400253200ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/filter_all_tests.ml'. Invalid request (no tests to run, filter skipped everything)! This run has ID `'. alcotest-1.7.0/test/e2e/alcotest/failing/filter_all_tests.ml000066400000000000000000000004071437617477400241340ustar00rootroot00000000000000(** Ensure that filters which eliminate all tests are rejected. *) let () = Alcotest.run __FILE__ [ ("foo", [ Alcotest.test_case "1" `Quick (fun () -> assert false) ]); ("bar", [ Alcotest.test_case "2" `Quick (fun () -> assert false) ]); ] alcotest-1.7.0/test/e2e/alcotest/failing/filter_all_tests.opts000066400000000000000000000000131437617477400245020ustar00rootroot00000000000000test bar 1 alcotest-1.7.0/test/e2e/alcotest/failing/invalid_arg_in_test-js.expected000066400000000000000000000023311437617477400264020ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/invalid_arg_in_test.ml'. This run has ID `'. [OK] test-a 0 Passing. > [FAIL] test-a 1 Failing. [OK] test-b 0 Another pass. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] test-a 1 Failing. │ └──────────────────────────────────────────────────────────────────────────────┘ [invalid] Failing test Logs saved to `/_build/_tests//test-a.001.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 3 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/invalid_arg_in_test.expected000066400000000000000000000023731437617477400257760ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/invalid_arg_in_test.ml'. This run has ID `'. [OK] test-a 0 Passing. > [FAIL] test-a 1 Failing. [OK] test-b 0 Another pass. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] test-a 1 Failing. │ └──────────────────────────────────────────────────────────────────────────────┘ [invalid] Failing test Logs saved to `/_build/_tests//test-a.001.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 3 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/invalid_arg_in_test.ml000066400000000000000000000004541437617477400246030ustar00rootroot00000000000000let () = let open Alcotest in run __FILE__ [ ( "test-a", [ test_case "Passing" `Quick (fun () -> ()); test_case "Failing" `Quick (fun () -> invalid_arg "Failing test"); ] ); ("test-b", [ test_case "Another pass" `Quick (fun () -> ()) ]); ] alcotest-1.7.0/test/e2e/alcotest/failing/long_test_case_name-js.expected000066400000000000000000000023161437617477400263720ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/long_test_case_name.ml'. This run has ID `'. [OK] test-a 0 Passing. > [FAIL] test-a 1 Lorem ipsum dolor sit amet, consectetur a... ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] test-a 1 Lorem ipsum dolor sit amet, consectetu... │ └──────────────────────────────────────────────────────────────────────────────┘ ASSERT Failed FAIL Failed Logs saved to `/_build/_tests//test-a.001.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 2 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/long_test_case_name.expected000066400000000000000000000023341437617477400257600ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/long_test_case_name.ml'. This run has ID `'. [OK] test-a 0 Passing. > [FAIL] test-a 1 Lorem ipsum dolor sit amet, consectetur a... ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] test-a 1 Lorem ipsum dolor sit amet, consectetu... │ └──────────────────────────────────────────────────────────────────────────────┘ ASSERT Failed FAIL Failed Logs saved to `/_build/_tests//test-a.001.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 2 tests run. alcotest-1.7.0/test/e2e/alcotest/failing/long_test_case_name.ml000066400000000000000000000006741437617477400245740ustar00rootroot00000000000000let () = let open Alcotest in run __FILE__ [ ( "test-a", [ test_case "Passing" `Quick (fun () -> ()); test_case "Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ Suspendisse mollis, orci sed venenatis efficitur, eros est \ imperdiet purus, sit amet tincidunt massa diam ut elit." `Quick (fun () -> Alcotest.fail "Failed"); ] ); ] alcotest-1.7.0/test/e2e/alcotest/failing/outside_runner-js.expected000066400000000000000000000002401437617477400254400ustar00rootroot00000000000000ASSERT Passing assertion ASSERT Failing assertion Fatal error: exception Alcotest assertion failure FAIL Failing assertion Expected: `1' Received: `2' alcotest-1.7.0/test/e2e/alcotest/failing/outside_runner.expected000066400000000000000000000002401437617477400250260ustar00rootroot00000000000000ASSERT Passing assertion ASSERT Failing assertion Fatal error: exception Alcotest assertion failure FAIL Failing assertion Expected: `1' Received: `2' alcotest-1.7.0/test/e2e/alcotest/failing/outside_runner.ml000066400000000000000000000004341437617477400236420ustar00rootroot00000000000000(** Capture the behaviour of the assertion functions when not running inside [Alcotest.run]. *) let () = Alcotest.(check int) "" 1 1 (* Empty passing assertion; should be silent *); Alcotest.(check int) "Passing assertion" 1 1; Alcotest.(check int) "Failing assertion" 1 2 alcotest-1.7.0/test/e2e/alcotest/failing/tail_errors_limit-js.expected000066400000000000000000000025761437617477400261340ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/tail_errors_limit.ml'. This run has ID `'. > [FAIL] failing 0 test. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] failing 0 test. │ └──────────────────────────────────────────────────────────────────────────────┘ ... (omitting 92 lines) output line 93 output line 94 output line 95 output line 96 output line 97 output line 98 output line 99 output line 100 ASSERT Logs should be 10 lines long, including this line (omitting 92). FAIL Logs should be 10 lines long, including this line (omitting 92). Logs saved to `/_build/_tests//failing.000.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/failing/tail_errors_limit.expected000066400000000000000000000025761437617477400255220ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/tail_errors_limit.ml'. This run has ID `'. > [FAIL] failing 0 test. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] failing 0 test. │ └──────────────────────────────────────────────────────────────────────────────┘ ... (omitting 92 lines) output line 93 output line 94 output line 95 output line 96 output line 97 output line 98 output line 99 output line 100 ASSERT Logs should be 10 lines long, including this line (omitting 92). FAIL Logs should be 10 lines long, including this line (omitting 92). Logs saved to `/_build/_tests//failing.000.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/failing/tail_errors_limit.ml000066400000000000000000000005331437617477400243200ustar00rootroot00000000000000let test_error_output () = for i = 1 to 100 do Printf.printf "output line %i\n" i done; Alcotest.fail "Logs should be 10 lines long, including this line (omitting 92)." let () = let open Alcotest in run ~record_backtrace:false ~tail_errors:(`Limit 10) __FILE__ [ ("failing", [ test_case "test" `Quick test_error_output ]) ] alcotest-1.7.0/test/e2e/alcotest/failing/tail_errors_unlimited-js.expected000066400000000000000000000052151437617477400270010ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/tail_errors_unlimited.ml'. This run has ID `'. > [FAIL] failing 0 test. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] failing 0 test. │ └──────────────────────────────────────────────────────────────────────────────┘ output line 1 output line 2 output line 3 output line 4 output line 5 output line 6 output line 7 output line 8 output line 9 output line 10 output line 11 output line 12 output line 13 output line 14 output line 15 output line 16 output line 17 output line 18 output line 19 output line 20 output line 21 output line 22 output line 23 output line 24 output line 25 output line 26 output line 27 output line 28 output line 29 output line 30 output line 31 output line 32 output line 33 output line 34 output line 35 output line 36 output line 37 output line 38 output line 39 output line 40 output line 41 output line 42 output line 43 output line 44 output line 45 output line 46 output line 47 output line 48 output line 49 output line 50 output line 51 output line 52 output line 53 output line 54 output line 55 output line 56 output line 57 output line 58 output line 59 output line 60 output line 61 output line 62 output line 63 output line 64 output line 65 output line 66 output line 67 output line 68 output line 69 output line 70 output line 71 output line 72 output line 73 output line 74 output line 75 output line 76 output line 77 output line 78 output line 79 output line 80 output line 81 output line 82 output line 83 output line 84 output line 85 output line 86 output line 87 output line 88 output line 89 output line 90 output line 91 output line 92 output line 93 output line 94 output line 95 output line 96 output line 97 output line 98 output line 99 output line 100 ASSERT Logs above should be 101 lines long. FAIL Logs above should be 101 lines long. Logs saved to `/_build/_tests//failing.000.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/failing/tail_errors_unlimited.expected000066400000000000000000000052331437617477400263670ustar00rootroot00000000000000Testing `test/e2e/alcotest/failing/tail_errors_unlimited.ml'. This run has ID `'. > [FAIL] failing 0 test. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] failing 0 test. │ └──────────────────────────────────────────────────────────────────────────────┘ output line 1 output line 2 output line 3 output line 4 output line 5 output line 6 output line 7 output line 8 output line 9 output line 10 output line 11 output line 12 output line 13 output line 14 output line 15 output line 16 output line 17 output line 18 output line 19 output line 20 output line 21 output line 22 output line 23 output line 24 output line 25 output line 26 output line 27 output line 28 output line 29 output line 30 output line 31 output line 32 output line 33 output line 34 output line 35 output line 36 output line 37 output line 38 output line 39 output line 40 output line 41 output line 42 output line 43 output line 44 output line 45 output line 46 output line 47 output line 48 output line 49 output line 50 output line 51 output line 52 output line 53 output line 54 output line 55 output line 56 output line 57 output line 58 output line 59 output line 60 output line 61 output line 62 output line 63 output line 64 output line 65 output line 66 output line 67 output line 68 output line 69 output line 70 output line 71 output line 72 output line 73 output line 74 output line 75 output line 76 output line 77 output line 78 output line 79 output line 80 output line 81 output line 82 output line 83 output line 84 output line 85 output line 86 output line 87 output line 88 output line 89 output line 90 output line 91 output line 92 output line 93 output line 94 output line 95 output line 96 output line 97 output line 98 output line 99 output line 100 ASSERT Logs above should be 101 lines long. FAIL Logs above should be 101 lines long. Logs saved to `/_build/_tests//failing.000.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/failing/tail_errors_unlimited.ml000066400000000000000000000004421437617477400251730ustar00rootroot00000000000000let test_error_output () = for i = 1 to 100 do Printf.printf "output line %i\n" i done; Alcotest.fail "Logs above should be 101 lines long." let () = let open Alcotest in run ~tail_errors:`Unlimited __FILE__ [ ("failing", [ test_case "test" `Quick test_error_output ]) ] alcotest-1.7.0/test/e2e/alcotest/failing/unknown_option-js.expected000066400000000000000000000002251437617477400254650ustar00rootroot00000000000000unknown_option.: unknown option '--dry-runn'. Usage: unknown_option. [COMMAND] … Try 'unknown_option. --help' for more information. alcotest-1.7.0/test/e2e/alcotest/failing/unknown_option.expected000066400000000000000000000002251437617477400250530ustar00rootroot00000000000000unknown_option.: unknown option '--dry-runn'. Usage: unknown_option. [COMMAND] … Try 'unknown_option. --help' for more information. alcotest-1.7.0/test/e2e/alcotest/failing/unknown_option.ml000066400000000000000000000001451437617477400236630ustar00rootroot00000000000000let () = Alcotest.run __FILE__ [ ("alpha", [ Alcotest.test_case "1" `Quick (fun () -> ()) ]) ] alcotest-1.7.0/test/e2e/alcotest/failing/unknown_option.opts000066400000000000000000000000131437617477400242320ustar00rootroot00000000000000--dry-runn alcotest-1.7.0/test/e2e/alcotest/inside-dune/000077500000000000000000000000001437617477400210355ustar00rootroot00000000000000alcotest-1.7.0/test/e2e/alcotest/inside-dune/color-overridden.expected000066400000000000000000000006611437617477400260400ustar00rootroot00000000000000Testing `test/e2e/alcotest/inside-dune/test_color.ml'. This run has ID `'. [OK] alpha 0 Output may or may not contain ANSI escape ... [OK] alpha 1 according to whether or not [--color] is set. [OK] alpha 2 (See the corresponding [dune] file.). Full test results in `/_build/_tests/'. Test Successful in s. 3 tests run. alcotest-1.7.0/test/e2e/alcotest/inside-dune/dune000066400000000000000000000011071437617477400217120ustar00rootroot00000000000000(executable (name test_color) (libraries alcotest)) ;; Run `test_color` with `--color=auto` (rule (target color-overridden.actual) (action (with-outputs-to %{target} ;; `--color=auto` passed → output should _not_ be colored due to Dune's buffering (run %{dep:test_color.exe} --color=auto)))) (rule (target color-overridden.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:color-overridden.actual})))) (rule (alias runtest) (package alcotest) (action (diff color-overridden.expected color-overridden.processed))) alcotest-1.7.0/test/e2e/alcotest/inside-dune/test_color.ml000066400000000000000000000005351437617477400235470ustar00rootroot00000000000000let () = let open Alcotest in let id () = () in run __FILE__ [ ( "alpha", [ test_case "Output may or may not contain ANSI escape codes" `Quick id; test_case "according to whether or not [--color] is set." `Quick id; test_case "(See the corresponding [dune] file.)" `Quick id; ] ); ] alcotest-1.7.0/test/e2e/alcotest/passing/000077500000000000000000000000001437617477400202755ustar00rootroot00000000000000alcotest-1.7.0/test/e2e/alcotest/passing/and_exit_false-js.expected000066400000000000000000000004221437617477400253750ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/and_exit_false.ml'. This run has ID `'. [OK] test-a 0 Test case. Full test results in `/_build/_tests/'. Test Successful in s. 1 test run. Program execution continued! alcotest-1.7.0/test/e2e/alcotest/passing/and_exit_false.expected000066400000000000000000000004221437617477400247630ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/and_exit_false.ml'. This run has ID `'. [OK] test-a 0 Test case. Full test results in `/_build/_tests/'. Test Successful in s. 1 test run. Program execution continued! alcotest-1.7.0/test/e2e/alcotest/passing/and_exit_false.ml000066400000000000000000000003001437617477400235650ustar00rootroot00000000000000let () = let open Alcotest in let id () = () in run ~and_exit:false __FILE__ [ ("test-a", [ test_case "Test case" `Quick id ]) ]; Printf.printf "\n Program execution continued!%!" alcotest-1.7.0/test/e2e/alcotest/passing/and_exit_true-js.expected000066400000000000000000000003631437617477400252660ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/and_exit_true.ml'. This run has ID `'. [OK] test-a 0 Test case. Full test results in `/_build/_tests/'. Test Successful in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/passing/and_exit_true.expected000066400000000000000000000003631437617477400246540ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/and_exit_true.ml'. This run has ID `'. [OK] test-a 0 Test case. Full test results in `/_build/_tests/'. Test Successful in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/passing/and_exit_true.ml000066400000000000000000000003131437617477400234560ustar00rootroot00000000000000let () = let open Alcotest in let id () = () in run ~and_exit:true __FILE__ [ ("test-a", [ test_case "Test case" `Quick id ]) ]; Printf.printf "\n Should never be printed!%!"; assert false alcotest-1.7.0/test/e2e/alcotest/passing/assert_and_verbose-js.expected000066400000000000000000000010131437617477400262750ustar00rootroot00000000000000Testing `assert-and-verbose'. This run has ID `'. ASSERT alpha-0 check alpha-0 standard out [OK] alpha 0 check → stdout. alpha-1 standard out ASSERT alpha-1 check [OK] alpha 1 stdout → check. ASSERT alpha-2 check alpha-2 standard error [OK] alpha 2 check → stderr. beta-0 standard out ASSERT beta-0 check beta-0 standard error [OK] beta 0 stdout → check → stderr. Test Successful in s. 4 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/assert_and_verbose.expected000066400000000000000000000010131437617477400256630ustar00rootroot00000000000000Testing `assert-and-verbose'. This run has ID `'. ASSERT alpha-0 check alpha-0 standard out [OK] alpha 0 check → stdout. alpha-1 standard out ASSERT alpha-1 check [OK] alpha 1 stdout → check. ASSERT alpha-2 check alpha-2 standard error [OK] alpha 2 check → stderr. beta-0 standard out ASSERT beta-0 check beta-0 standard error [OK] beta 0 stdout → check → stderr. Test Successful in s. 4 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/assert_and_verbose.ml000066400000000000000000000020131437617477400244730ustar00rootroot00000000000000(** Test the interaction between ASSERT prints and the `--verbose` option. *) let () = let open Alcotest in run ~verbose:true "assert-and-verbose" [ ( "alpha", [ Alcotest.test_case "check → stdout" `Quick (fun () -> Alcotest.(check unit) "alpha-0 check" () (); Format.printf "alpha-0 standard out\n"); Alcotest.test_case "stdout → check" `Quick (fun () -> Format.printf "\nalpha-1 standard out\n"; Alcotest.(check unit) "alpha-1 check" () ()); Alcotest.test_case "check → stderr" `Quick (fun () -> Alcotest.(check unit) "alpha-2 check" () (); Format.eprintf "alpha-2 standard error\n"); ] ); ( "beta", [ Alcotest.test_case "stdout → check → stderr" `Quick (fun () -> Format.printf "\nbeta-0 standard out\n"; Alcotest.(check unit) "beta-0 check" () (); Format.eprintf "beta-0 standard error\n"); ] ); ] alcotest-1.7.0/test/e2e/alcotest/passing/assert_not_printed-js.expected000066400000000000000000000004741437617477400263450ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/assert_not_printed.ml'. This run has ID `'. [OK] alpha 0 0. [OK] alpha 1 1. [OK] beta 0 2. Full test results in `/_build/_tests/'. Test Successful in s. 3 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/assert_not_printed.expected000066400000000000000000000004741437617477400257330ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/assert_not_printed.ml'. This run has ID `'. [OK] alpha 0 0. [OK] alpha 1 1. [OK] beta 0 2. Full test results in `/_build/_tests/'. Test Successful in s. 3 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/assert_not_printed.ml000066400000000000000000000014071437617477400245370ustar00rootroot00000000000000(** Regression test for an issue in which stderr was not captured into the test logs due to Format buffers not being flushed. See https://github.com/mirage/alcotest/pull/228 for details. *) let () = let open Alcotest in let s tc = tc ^ ": SHOULD NOT BE PRINTED" in run __FILE__ [ ( "alpha", [ Alcotest.test_case "0" `Quick (fun () -> Alcotest.(check unit) (s "0") () ()); Alcotest.test_case "1" `Quick (fun () -> Format.eprintf "%s" (s "1"); Alcotest.(check unit) (s "1") () ()); ] ); ( "beta", [ Alcotest.test_case "2" `Quick (fun () -> Alcotest.(check unit) "2" () (); Format.eprintf "%s" (s "2")); ] ); ] alcotest-1.7.0/test/e2e/alcotest/passing/basic-js.expected000066400000000000000000000006231437617477400235140ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/basic.ml'. This run has ID `'. [OK] test-a 0 First test case. [OK] test-a 1 Second test case. [OK] test-b 0 Third test case. [OK] test-c 0 Fourth test case. Full test results in `/_build/_tests/'. Test Successful in s. 4 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/basic.expected000066400000000000000000000006231437617477400231020ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/basic.ml'. This run has ID `'. [OK] test-a 0 First test case. [OK] test-a 1 Second test case. [OK] test-b 0 Third test case. [OK] test-c 0 Fourth test case. Full test results in `/_build/_tests/'. Test Successful in s. 4 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/basic.ml000066400000000000000000000005251437617477400217120ustar00rootroot00000000000000let () = let open Alcotest in let id () = () in run __FILE__ [ ( "test-a", [ test_case "First test case" `Quick id; test_case "Second test case" `Quick id; ] ); ("test-b", [ test_case "Third test case" `Quick id ]); ("test-c", [ test_case "Fourth test case" `Slow id ]); ] alcotest-1.7.0/test/e2e/alcotest/passing/check_basic-js.expected000066400000000000000000000031411437617477400246470ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/check_basic.ml'. This run has ID `'. [OK] reflexive basic 0 unit. [OK] reflexive basic 1 bool. [OK] reflexive basic 2 int. [OK] reflexive basic 3 int32. [OK] reflexive basic 4 int64. [OK] reflexive basic 5 float. [OK] reflexive basic 6 char. [OK] reflexive basic 7 string. [OK] reflexive basic 8 bytes. [OK] reflexive composite 0 empty list. [OK] reflexive composite 1 non-empty list. [OK] reflexive composite 2 empty array. [OK] reflexive composite 3 non-empty array. [OK] reflexive composite 4 option some. [OK] reflexive composite 5 option none. [OK] reflexive composite 6 result ok. [OK] reflexive composite 7 result error. [OK] reflexive composite 8 pair. [OK] reflexive composite 9 triple. [OK] negation 0 checked exceptions. [OK] negation 1 negated testables. [OK] fuzzy equality 0 float thresholds. [OK] fuzzy equality 1 sorted list. [OK] labeled check 0 passing. Full test results in `/_build/_tests/'. Test Successful in s. 24 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/check_basic.expected000066400000000000000000000031411437617477400242350ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/check_basic.ml'. This run has ID `'. [OK] reflexive basic 0 unit. [OK] reflexive basic 1 bool. [OK] reflexive basic 2 int. [OK] reflexive basic 3 int32. [OK] reflexive basic 4 int64. [OK] reflexive basic 5 float. [OK] reflexive basic 6 char. [OK] reflexive basic 7 string. [OK] reflexive basic 8 bytes. [OK] reflexive composite 0 empty list. [OK] reflexive composite 1 non-empty list. [OK] reflexive composite 2 empty array. [OK] reflexive composite 3 non-empty array. [OK] reflexive composite 4 option some. [OK] reflexive composite 5 option none. [OK] reflexive composite 6 result ok. [OK] reflexive composite 7 result error. [OK] reflexive composite 8 pair. [OK] reflexive composite 9 triple. [OK] negation 0 checked exceptions. [OK] negation 1 negated testables. [OK] fuzzy equality 0 float thresholds. [OK] fuzzy equality 1 sorted list. [OK] labeled check 0 passing. Full test results in `/_build/_tests/'. Test Successful in s. 24 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/check_basic.ml000066400000000000000000000051151437617477400230470ustar00rootroot00000000000000(* Check that v of type [typ] matches with itself *) let id_case typ typ_str v = Alcotest.test_case typ_str `Quick (fun () -> Alcotest.check typ typ_str v v) exception Zero exception One of int exception Two of int * char let checked_exceptions () = let check_refl name exn = Alcotest.check_raises name exn (fun () -> raise exn) in check_refl "0-tuple" Zero; check_refl "1-tuple" (One 1); check_refl "2-tuple" (Two (1, 'a')) let negated_testables () = Alcotest.(check (neg reject)) "!reject" () (); Alcotest.(check (neg @@ neg pass)) "!!pass" () (); Alcotest.(check (neg @@ neg @@ neg reject)) "!!!reject" () (); Alcotest.(check (neg char)) "!different" 'a' 'b' let float_threshold () = Alcotest.(check (float 1.0) "within threshold") 0.1 0.2; Alcotest.(check (neg (float 0.01)) "not within threshold") 0.1 0.2 let sorted_list () = let int_compare : int -> int -> int = compare in Alcotest.(check (slist int int_compare)) "sorted" [ 1; 2; 3 ] [ 3; 2; 1 ] let labeled_check () = Alcotest.(check' int) ~msg:"Foo" ~expected:1 ~actual:1 let () = let open Alcotest in run __FILE__ [ ( "reflexive basic", [ id_case unit "unit" (); id_case bool "bool" true; id_case int "int" 1; id_case int32 "int32" Int32.max_int; id_case int64 "int64" Int64.max_int; id_case (float 0.0) "float" 1.0; id_case char "char" 'a'; id_case string "string" "Lorem ipsum dolor sit amet."; id_case bytes "bytes" (Bytes.of_string "\x01\x02\x03"); ] ); ( "reflexive composite", [ id_case (list int) "empty list" []; id_case (list char) "non-empty list" [ 'a'; 'b'; 'c' ]; id_case (array unit) "empty array" [||]; id_case (array int64) "non-empty array" Int64.[| zero; max_int |]; id_case (option int) "option some" (Some 1); id_case (option int) "option none" None; id_case (result int unit) "result ok" (Ok 1); id_case (result int unit) "result error" (Error ()); id_case (pair int char) "pair" (1, 'a'); id_case (triple int bool string) "triple" (1, true, "a"); ] ); ( "negation", [ test_case "checked exceptions" `Quick checked_exceptions; test_case "negated testables" `Quick negated_testables; ] ); ( "fuzzy equality", [ test_case "float thresholds" `Quick float_threshold; test_case "sorted list" `Quick sorted_list; ] ); ("labeled check", [ test_case "passing" `Quick labeled_check ]); ] alcotest-1.7.0/test/e2e/alcotest/passing/cli_verbose-js.expected000066400000000000000000000002471437617477400247310ustar00rootroot00000000000000Testing `cli_verbose'. This run has ID `'. SHOULD APPEAR IN TEST OUTPUT [OK] alpha 0 0. Test Successful in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/passing/cli_verbose.expected000066400000000000000000000002471437617477400243170ustar00rootroot00000000000000Testing `cli_verbose'. This run has ID `'. SHOULD APPEAR IN TEST OUTPUT [OK] alpha 0 0. Test Successful in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/passing/cli_verbose.ml000066400000000000000000000005331437617477400231240ustar00rootroot00000000000000(** Ensures that the `--verbose` flag is passed from the CLI. *) let () = let open Alcotest in run ~verbose:false (* CLI flag should take priority over this option *) "cli_verbose" [ ( "alpha", [ test_case "0" `Quick (fun () -> Format.printf "SHOULD APPEAR IN TEST OUTPUT\n"); ] ); ] alcotest-1.7.0/test/e2e/alcotest/passing/cli_verbose.opts000066400000000000000000000000121437617477400234710ustar00rootroot00000000000000--verbose alcotest-1.7.0/test/e2e/alcotest/passing/compact-js.expected000066400000000000000000000004311437617477400240560ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/compact.ml'. This run has ID `'. ................................................................................ ................................................................................ .......................................... alcotest-1.7.0/test/e2e/alcotest/passing/compact.expected000066400000000000000000000004311437617477400234440ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/compact.ml'. This run has ID `'. ................................................................................ ................................................................................ .......................................... alcotest-1.7.0/test/e2e/alcotest/passing/compact.ml000066400000000000000000000005341437617477400222570ustar00rootroot00000000000000open Alcotest_stdlib_ext let () = let open Alcotest in let id () = () in run __FILE__ [ ( "test-a", List.init 200 (fun i -> test_case (Format.sprintf "Test case #%d" i) `Quick id) ); ("test-b", [ test_case "Third test case" `Quick id ]); ("test-c", [ test_case "Fourth test case" `Slow id ]); ] alcotest-1.7.0/test/e2e/alcotest/passing/compact.opts000066400000000000000000000000121437617477400226230ustar00rootroot00000000000000--compact alcotest-1.7.0/test/e2e/alcotest/passing/dune000066400000000000000000000005521437617477400211550ustar00rootroot00000000000000(env (_ (env-vars (ALCOTEST_COLOR auto)))) (include dune.inc) (rule (targets dune.gen) (deps (source_tree .)) (action (with-stdout-to %{targets} (run ../../gen_dune_rules.exe --package "alcotest" --libraries "alcotest.engine" --js)))) (rule (alias runtest) (package alcotest) (action (diff dune.inc dune.gen))) alcotest-1.7.0/test/e2e/alcotest/passing/dune.inc000066400000000000000000000466171437617477400217410ustar00rootroot00000000000000(executables (names and_exit_false and_exit_true assert_and_verbose assert_not_printed basic check_basic cli_verbose compact empty_test_name filter_name filter_name_regex filter_range isatty json_output list_tests only_monadic_effects quick_only quick_only_regex separator_testname skip_in_test unicode_testname verbose_newlines ) (libraries alcotest alcotest.stdlib_ext alcotest.engine) (modes exe js) (modules and_exit_false and_exit_true assert_and_verbose assert_not_printed basic check_basic cli_verbose compact empty_test_name filter_name filter_name_regex filter_range isatty json_output list_tests only_monadic_effects quick_only quick_only_regex separator_testname skip_in_test unicode_testname verbose_newlines ) ) (rule (target and_exit_false.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:and_exit_false.exe}))))) (rule (target and_exit_false.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:and_exit_false.actual})))) (rule (alias runtest) (package alcotest) (action (diff and_exit_false.expected and_exit_false.processed))) (rule (target and_exit_false-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:and_exit_false.bc.js}))))) (rule (target and_exit_false-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:and_exit_false-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff and_exit_false-js.expected and_exit_false-js.processed))) (rule (target and_exit_true.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:and_exit_true.exe}))))) (rule (target and_exit_true.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:and_exit_true.actual})))) (rule (alias runtest) (package alcotest) (action (diff and_exit_true.expected and_exit_true.processed))) (rule (target and_exit_true-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:and_exit_true.bc.js}))))) (rule (target and_exit_true-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:and_exit_true-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff and_exit_true-js.expected and_exit_true-js.processed))) (rule (target assert_and_verbose.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:assert_and_verbose.exe}))))) (rule (target assert_and_verbose.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:assert_and_verbose.actual})))) (rule (alias runtest) (package alcotest) (action (diff assert_and_verbose.expected assert_and_verbose.processed))) (rule (target assert_and_verbose-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:assert_and_verbose.bc.js}))))) (rule (target assert_and_verbose-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:assert_and_verbose-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff assert_and_verbose-js.expected assert_and_verbose-js.processed))) (rule (target assert_not_printed.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:assert_not_printed.exe}))))) (rule (target assert_not_printed.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:assert_not_printed.actual})))) (rule (alias runtest) (package alcotest) (action (diff assert_not_printed.expected assert_not_printed.processed))) (rule (target assert_not_printed-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:assert_not_printed.bc.js}))))) (rule (target assert_not_printed-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:assert_not_printed-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff assert_not_printed-js.expected assert_not_printed-js.processed))) (rule (target basic.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:basic.exe}))))) (rule (target basic.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:basic.actual})))) (rule (alias runtest) (package alcotest) (action (diff basic.expected basic.processed))) (rule (target basic-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:basic.bc.js}))))) (rule (target basic-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:basic-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff basic-js.expected basic-js.processed))) (rule (target check_basic.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:check_basic.exe}))))) (rule (target check_basic.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:check_basic.actual})))) (rule (alias runtest) (package alcotest) (action (diff check_basic.expected check_basic.processed))) (rule (target check_basic-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:check_basic.bc.js}))))) (rule (target check_basic-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:check_basic-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff check_basic-js.expected check_basic-js.processed))) (rule (target cli_verbose.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:cli_verbose.exe} --verbose))))) (rule (target cli_verbose.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:cli_verbose.actual})))) (rule (alias runtest) (package alcotest) (action (diff cli_verbose.expected cli_verbose.processed))) (rule (target cli_verbose-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:cli_verbose.bc.js} --verbose))))) (rule (target cli_verbose-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:cli_verbose-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff cli_verbose-js.expected cli_verbose-js.processed))) (rule (target compact.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:compact.exe} --compact))))) (rule (target compact.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:compact.actual})))) (rule (alias runtest) (package alcotest) (action (diff compact.expected compact.processed))) (rule (target compact-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:compact.bc.js} --compact))))) (rule (target compact-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:compact-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff compact-js.expected compact-js.processed))) (rule (target empty_test_name.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:empty_test_name.exe}))))) (rule (target empty_test_name.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:empty_test_name.actual})))) (rule (alias runtest) (package alcotest) (action (diff empty_test_name.expected empty_test_name.processed))) (rule (target empty_test_name-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:empty_test_name.bc.js}))))) (rule (target empty_test_name-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:empty_test_name-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff empty_test_name-js.expected empty_test_name-js.processed))) (rule (target filter_name.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:filter_name.exe}))))) (rule (target filter_name.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:filter_name.actual})))) (rule (alias runtest) (package alcotest) (action (diff filter_name.expected filter_name.processed))) (rule (target filter_name-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:filter_name.bc.js}))))) (rule (target filter_name-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:filter_name-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff filter_name-js.expected filter_name-js.processed))) (rule (target filter_name_regex.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:filter_name_regex.exe}))))) (rule (target filter_name_regex.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:filter_name_regex.actual})))) (rule (alias runtest) (package alcotest) (action (diff filter_name_regex.expected filter_name_regex.processed))) (rule (target filter_name_regex-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:filter_name_regex.bc.js}))))) (rule (target filter_name_regex-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:filter_name_regex-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff filter_name_regex-js.expected filter_name_regex-js.processed))) (rule (target filter_range.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:filter_range.exe} test main 0..1,1-3,5))))) (rule (target filter_range.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:filter_range.actual})))) (rule (alias runtest) (package alcotest) (action (diff filter_range.expected filter_range.processed))) (rule (target filter_range-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:filter_range.bc.js} test main 0..1,1-3,5))))) (rule (target filter_range-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:filter_range-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff filter_range-js.expected filter_range-js.processed))) (rule (target isatty.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:isatty.exe}))))) (rule (target isatty.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:isatty.actual})))) (rule (alias runtest) (package alcotest) (action (diff isatty.expected isatty.processed))) (rule (target isatty-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:isatty.bc.js}))))) (rule (target isatty-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:isatty-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff isatty-js.expected isatty-js.processed))) (rule (target json_output.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:json_output.exe}))))) (rule (target json_output.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:json_output.actual})))) (rule (alias runtest) (package alcotest) (action (diff json_output.expected json_output.processed))) (rule (target json_output-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:json_output.bc.js}))))) (rule (target json_output-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:json_output-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff json_output-js.expected json_output-js.processed))) (rule (target list_tests.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:list_tests.exe}))))) (rule (target list_tests.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:list_tests.actual})))) (rule (alias runtest) (package alcotest) (action (diff list_tests.expected list_tests.processed))) (rule (target list_tests-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:list_tests.bc.js}))))) (rule (target list_tests-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:list_tests-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff list_tests-js.expected list_tests-js.processed))) (rule (target only_monadic_effects.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:only_monadic_effects.exe}))))) (rule (target only_monadic_effects.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:only_monadic_effects.actual})))) (rule (alias runtest) (package alcotest) (action (diff only_monadic_effects.expected only_monadic_effects.processed))) (rule (target only_monadic_effects-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:only_monadic_effects.bc.js}))))) (rule (target only_monadic_effects-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:only_monadic_effects-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff only_monadic_effects-js.expected only_monadic_effects-js.processed))) (rule (target quick_only.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:quick_only.exe}))))) (rule (target quick_only.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:quick_only.actual})))) (rule (alias runtest) (package alcotest) (action (diff quick_only.expected quick_only.processed))) (rule (target quick_only-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:quick_only.bc.js}))))) (rule (target quick_only-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:quick_only-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff quick_only-js.expected quick_only-js.processed))) (rule (target quick_only_regex.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:quick_only_regex.exe}))))) (rule (target quick_only_regex.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:quick_only_regex.actual})))) (rule (alias runtest) (package alcotest) (action (diff quick_only_regex.expected quick_only_regex.processed))) (rule (target quick_only_regex-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:quick_only_regex.bc.js}))))) (rule (target quick_only_regex-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:quick_only_regex-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff quick_only_regex-js.expected quick_only_regex-js.processed))) (rule (target separator_testname.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:separator_testname.exe}))))) (rule (target separator_testname.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:separator_testname.actual})))) (rule (alias runtest) (package alcotest) (action (diff separator_testname.expected separator_testname.processed))) (rule (target separator_testname-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:separator_testname.bc.js}))))) (rule (target separator_testname-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:separator_testname-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff separator_testname-js.expected separator_testname-js.processed))) (rule (target skip_in_test.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:skip_in_test.exe}))))) (rule (target skip_in_test.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:skip_in_test.actual})))) (rule (alias runtest) (package alcotest) (action (diff skip_in_test.expected skip_in_test.processed))) (rule (target skip_in_test-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:skip_in_test.bc.js}))))) (rule (target skip_in_test-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:skip_in_test-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff skip_in_test-js.expected skip_in_test-js.processed))) (rule (target unicode_testname.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:unicode_testname.exe}))))) (rule (target unicode_testname.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:unicode_testname.actual})))) (rule (alias runtest) (package alcotest) (action (diff unicode_testname.expected unicode_testname.processed))) (rule (target unicode_testname-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:unicode_testname.bc.js}))))) (rule (target unicode_testname-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:unicode_testname-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff unicode_testname-js.expected unicode_testname-js.processed))) (rule (target verbose_newlines.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run %{dep:verbose_newlines.exe}))))) (rule (target verbose_newlines.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:verbose_newlines.actual})))) (rule (alias runtest) (package alcotest) (action (diff verbose_newlines.expected verbose_newlines.processed))) (rule (target verbose_newlines-js.actual) (action (with-outputs-to %{target} (with-accepted-exit-codes (or 0 124 125) (run node %{dep:verbose_newlines.bc.js}))))) (rule (target verbose_newlines-js.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:verbose_newlines-js.actual})))) (rule (alias runtest-js) (package alcotest) (action (diff verbose_newlines-js.expected verbose_newlines-js.processed))) alcotest-1.7.0/test/e2e/alcotest/passing/empty_test_name-js.expected000066400000000000000000000003471437617477400256330ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/empty_test_name.ml'. This run has ID `'. [OK] 0 1. Full test results in `/_build/_tests/'. Test Successful in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/passing/empty_test_name.expected000066400000000000000000000003471437617477400252210ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/empty_test_name.ml'. This run has ID `'. [OK] 0 1. Full test results in `/_build/_tests/'. Test Successful in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/passing/empty_test_name.ml000066400000000000000000000001401437617477400240170ustar00rootroot00000000000000let () = Alcotest.run __FILE__ [ ("", [ Alcotest.test_case "1" `Quick (fun () -> ()) ]) ] alcotest-1.7.0/test/e2e/alcotest/passing/filter_name-js.expected000066400000000000000000000005501437617477400247170ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/filter_name.ml'. This run has ID `'. [OK] test-a 0 First test case. [OK] test-a 1 Second test case. [SKIP] test-b 0 Skipped failing test. Full test results in `/_build/_tests/'. Test Successful in s. 2 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/filter_name.expected000066400000000000000000000005501437617477400243050ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/filter_name.ml'. This run has ID `'. [OK] test-a 0 First test case. [OK] test-a 1 Second test case. [SKIP] test-b 0 Skipped failing test. Full test results in `/_build/_tests/'. Test Successful in s. 2 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/filter_name.ml000066400000000000000000000005671437617477400231240ustar00rootroot00000000000000let () = let open Alcotest in let id () = () in run ~argv:[| ""; "test"; "test-a" |] __FILE__ [ ( "test-a", [ test_case "First test case" `Quick id; test_case "Second test case" `Quick id; ] ); ( "test-b", [ test_case "Skipped failing test" `Quick (fun () -> invalid_arg "boom"); ] ); ] alcotest-1.7.0/test/e2e/alcotest/passing/filter_name_regex-js.expected000066400000000000000000000007251437617477400261150ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/filter_name_regex.ml'. This run has ID `'. [OK] basic-run-a 0 Executed. [OK] basic-run-a 1 Also executed. [SKIP] basic-run-b 0 Skipped. [OK] basic-run-c 0 Executed. [SKIP] complex-run-a 0 Skipped. Full test results in `/_build/_tests/'. Test Successful in s. 3 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/filter_name_regex.expected000066400000000000000000000007251437617477400255030ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/filter_name_regex.ml'. This run has ID `'. [OK] basic-run-a 0 Executed. [OK] basic-run-a 1 Also executed. [SKIP] basic-run-b 0 Skipped. [OK] basic-run-c 0 Executed. [SKIP] complex-run-a 0 Skipped. Full test results in `/_build/_tests/'. Test Successful in s. 3 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/filter_name_regex.ml000066400000000000000000000006411437617477400243070ustar00rootroot00000000000000let () = let open Alcotest in let id () = () in run ~argv:[| ""; "test"; "basic-.*-(a|c)" |] __FILE__ [ ( "basic-run-a", [ test_case "Executed" `Quick id; test_case "Also executed" `Quick id ] ); ("basic-run-b", [ test_case "Skipped" `Quick id ]); ("basic-run-c", [ test_case "Executed" `Quick id ]); ("complex-run-a", [ test_case "Skipped" `Quick id ]); ] alcotest-1.7.0/test/e2e/alcotest/passing/filter_range-js.expected000066400000000000000000000007001437617477400250700ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/filter_range.ml'. This run has ID `'. [OK] main 0 test-0. [OK] main 1 test-1. [OK] main 2 test-2. [OK] main 3 test-3. [SKIP] main 4 test-4. [OK] main 5 test-5. Full test results in `/_build/_tests/'. Test Successful in s. 5 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/filter_range.expected000066400000000000000000000007001437617477400244560ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/filter_range.ml'. This run has ID `'. [OK] main 0 test-0. [OK] main 1 test-1. [OK] main 2 test-2. [OK] main 3 test-3. [SKIP] main 4 test-4. [OK] main 5 test-5. Full test results in `/_build/_tests/'. Test Successful in s. 5 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/filter_range.ml000066400000000000000000000006251437617477400232730ustar00rootroot00000000000000open Alcotest_stdlib_ext let () = let should_run i = (* Corresponds to ranges specified in [filter_range.opts]. *) List.mem i [ 0; 1; 2; 3; 5 ] in let tests = List.init 6 (fun i -> Alcotest.test_case (Printf.sprintf "test-%d" i) `Quick (fun () -> if not (should_run i) then failwith "Test should have been skipped")) in Alcotest.run __FILE__ [ ("main", tests) ] alcotest-1.7.0/test/e2e/alcotest/passing/filter_range.opts000066400000000000000000000000251437617477400236420ustar00rootroot00000000000000test main 0..1,1-3,5 alcotest-1.7.0/test/e2e/alcotest/passing/isatty-js.expected000066400000000000000000000011521437617477400237460ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/isatty.ml'. This run has ID `'. ... test-a 0 First test case. [OK] test-a 0 First test case. ... test-a 1 Second test case. [OK] test-a 1 Second test case. ... test-b 0 Third test case. [OK] test-b 0 Third test case. ... test-c 0 Fourth test case. [OK] test-c 0 Fourth test case. Full test results in `/_build/_tests/'. Test Successful in s. 4 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/isatty.expected000066400000000000000000000011521437617477400233340ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/isatty.ml'. This run has ID `'. ... test-a 0 First test case. [OK] test-a 0 First test case. ... test-a 1 Second test case. [OK] test-a 1 Second test case. ... test-b 0 Third test case. [OK] test-b 0 Third test case. ... test-c 0 Fourth test case. [OK] test-c 0 Fourth test case. Full test results in `/_build/_tests/'. Test Successful in s. 4 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/isatty.ml000066400000000000000000000012671437617477400221520ustar00rootroot00000000000000(** Test of the "in progress" output lines printed for each test-case (which are only shown when standard output is a TTY). *) module Platform (M : Alcotest_engine.Monad.S) = struct include Alcotest.Unix_platform (M) let stdout_isatty () = true end module Alcotest = Alcotest_engine.V1.Core.Make (Platform) (Alcotest_engine.Monad.Identity) let () = let open Alcotest in let id () = () in run __FILE__ [ ( "test-a", [ test_case "First test case" `Quick id; test_case "Second test case" `Quick id; ] ); ("test-b", [ test_case "Third test case" `Quick id ]); ("test-c", [ test_case "Fourth test case" `Slow id ]); ] alcotest-1.7.0/test/e2e/alcotest/passing/json_output-js.expected000066400000000000000000000002161437617477400250220ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/json_output.ml'. This run has ID `'. { "success": 3, "failures": 0, "time": } alcotest-1.7.0/test/e2e/alcotest/passing/json_output.expected000066400000000000000000000002161437617477400244100ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/json_output.ml'. This run has ID `'. { "success": 3, "failures": 0, "time": } alcotest-1.7.0/test/e2e/alcotest/passing/json_output.ml000066400000000000000000000004611437617477400232210ustar00rootroot00000000000000let () = let open Alcotest in let id () = () in run ~argv:[| ""; "--json" |] __FILE__ [ ( "test-a", [ test_case "First test case" `Quick id; test_case "Second test case" `Quick id; ] ); ("test-b", [ test_case "Third test case" `Quick id ]); ] alcotest-1.7.0/test/e2e/alcotest/passing/list_tests-js.expected000066400000000000000000000002011437617477400246200ustar00rootroot00000000000000test-a 0 alpha. test-a 1 beta. test-b 0 lorem ipsum dolor sit amet consectutor adipiscing elit. alcotest-1.7.0/test/e2e/alcotest/passing/list_tests.expected000066400000000000000000000002011437617477400242060ustar00rootroot00000000000000test-a 0 alpha. test-a 1 beta. test-b 0 lorem ipsum dolor sit amet consectutor adipiscing elit. alcotest-1.7.0/test/e2e/alcotest/passing/list_tests.ml000066400000000000000000000006231437617477400230250ustar00rootroot00000000000000let () = let open Alcotest in let failtest () = invalid_arg "This test should never be run" in run ~argv:[| ""; "list" |] __FILE__ [ ( "test-a", [ test_case "alpha" `Quick failtest; test_case "beta" `Quick failtest ] ); ( "test-b", [ test_case "lorem ipsum dolor sit amet consectutor adipiscing elit" `Quick failtest; ] ); ] alcotest-1.7.0/test/e2e/alcotest/passing/only_monadic_effects.expected000066400000000000000000000000001437617477400261600ustar00rootroot00000000000000alcotest-1.7.0/test/e2e/alcotest/passing/only_monadic_effects.ml000066400000000000000000000032771437617477400250120ustar00rootroot00000000000000(** Ensures that {!Alcotest.run} does not print when evaluated at monadic type (until the resulting computation is explicitly run). We require that effects introduced by tests (whether during evaluation {i or} during running the computation) are not partially observable during application of {!Alcotest.run}. This is because such behaviour could be relied upon as part of the public API, when in fact it is dependent on internal implementation details. This is tested by building a runner over the [Terminal] monad, which reduces to [()] immediately on evaluation and so discovers any effects in the evaluation of the bottom-most value in the computation. This is a regression test for a bug in which test effects were partially observable during evaluation of the computation. See https://github.com/mirage/alcotest/pull/228 for more details. *) module Terminal : Alcotest_engine.Monad.S = struct type 'a t = unit let return _ = () let bind () _ = () let catch f on_error = match f () with x -> x | exception ex -> on_error ex end module Runner = Alcotest_engine.V1.Core.Make (Alcotest.Unix_platform) (Terminal) let () = let (_ : unit Terminal.t) = Runner.run ~verbose:true __FILE__ [ ( "alpha", [ Runner.test_case "check + stdout + stderr" `Quick (fun () -> Alcotest.(check unit) "SHOULD NOT BE PRINTED" () (); Format.printf "stdout: SHOULD NOT BE PRINTED\n"; Format.eprintf "stderr: SHOULD NOT BE PRINTED\n"; assert false); ] ); ] in Format.pp_print_flush Format.std_formatter (); Format.pp_print_flush Format.err_formatter (); () alcotest-1.7.0/test/e2e/alcotest/passing/quick_only-js.expected000066400000000000000000000005541437617477400246130ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/quick_only.ml'. This run has ID `'. [OK] test-a 0 Quick. [SKIP] test-a 1 Slow. [SKIP] test-b 0 Slow. [OK] test-b 1 Quick. Full test results in `/_build/_tests/'. Test Successful in s. 2 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/quick_only.expected000066400000000000000000000005541437617477400242010ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/quick_only.ml'. This run has ID `'. [OK] test-a 0 Quick. [SKIP] test-a 1 Slow. [SKIP] test-b 0 Slow. [OK] test-b 1 Quick. Full test results in `/_build/_tests/'. Test Successful in s. 2 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/quick_only.ml000066400000000000000000000004051437617477400230030ustar00rootroot00000000000000let () = let open Alcotest in let id () = () in run ~argv:[| ""; "--quick" |] __FILE__ [ ("test-a", [ test_case "Quick" `Quick id; test_case "Slow" `Slow id ]); ("test-b", [ test_case "Slow" `Slow id; test_case "Quick" `Quick id ]); ] alcotest-1.7.0/test/e2e/alcotest/passing/quick_only_regex-js.expected000066400000000000000000000006571437617477400260110ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/quick_only_regex.ml'. This run has ID `'. [OK] test-a 0 Quick & passes filter. [SKIP] test-a 1 Slow & passes filter. [SKIP] test-b 0 Slow & fails filter. [SKIP] test-b 1 Quick & fails filter. Full test results in `/_build/_tests/'. Test Successful in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/passing/quick_only_regex.expected000066400000000000000000000006571437617477400253770ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/quick_only_regex.ml'. This run has ID `'. [OK] test-a 0 Quick & passes filter. [SKIP] test-a 1 Slow & passes filter. [SKIP] test-b 0 Slow & fails filter. [SKIP] test-b 1 Quick & fails filter. Full test results in `/_build/_tests/'. Test Successful in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/passing/quick_only_regex.ml000066400000000000000000000006511437617477400242000ustar00rootroot00000000000000let () = let open Alcotest in let id () = () in run ~argv:[| ""; "test"; "--quick"; ".*-a" |] __FILE__ [ ( "test-a", [ test_case "Quick & passes filter" `Quick id; test_case "Slow & passes filter" `Slow id; ] ); ( "test-b", [ test_case "Slow & fails filter" `Slow id; test_case "Quick & fails filter" `Quick id; ] ); ] alcotest-1.7.0/test/e2e/alcotest/passing/separator_testname-js.expected000066400000000000000000000004061437617477400263320ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/separator_testname.ml'. This run has ID `'. [OK] with/separator 0 First test case. Full test results in `/_build/_tests/'. Test Successful in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/passing/separator_testname.expected000066400000000000000000000004061437617477400257200ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/separator_testname.ml'. This run has ID `'. [OK] with/separator 0 First test case. Full test results in `/_build/_tests/'. Test Successful in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/passing/separator_testname.ml000066400000000000000000000002211437617477400245220ustar00rootroot00000000000000let () = Alcotest.run __FILE__ [ ( "with/separator", [ Alcotest.test_case "First test case" `Quick (fun () -> ()) ] ); ] alcotest-1.7.0/test/e2e/alcotest/passing/skip_in_test-js.expected000066400000000000000000000004521437617477400251260ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/skip_in_test.ml'. This run has ID `'. [OK] test-a 0 Run test case. [SKIP] test-a 1 Skip test case. Full test results in `/_build/_tests/'. Test Successful in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/passing/skip_in_test.expected000066400000000000000000000004521437617477400245140ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/skip_in_test.ml'. This run has ID `'. [OK] test-a 0 Run test case. [SKIP] test-a 1 Skip test case. Full test results in `/_build/_tests/'. Test Successful in s. 1 test run. alcotest-1.7.0/test/e2e/alcotest/passing/skip_in_test.ml000066400000000000000000000003311437617477400233170ustar00rootroot00000000000000let () = let open Alcotest in let id () = () in run __FILE__ [ ( "test-a", [ test_case "Run test case" `Quick id; test_case "Skip test case" `Quick skip; ] ); ] alcotest-1.7.0/test/e2e/alcotest/passing/unicode_testname-js.expected000066400000000000000000000005471437617477400257660ustar00rootroot00000000000000Testing `Suite name containing file separators / and non-ASCII characters 🔥'. This run has ID `'. [OK] 🔥 0 Non ASCII unicode character. [OK] 🔥a-b 0 Non ASCII and ASCII characters. Full test results in `/_build/_tests/'. Test Successful in s. 2 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/unicode_testname.expected000066400000000000000000000005471437617477400253540ustar00rootroot00000000000000Testing `Suite name containing file separators / and non-ASCII characters 🔥'. This run has ID `'. [OK] 🔥 0 Non ASCII unicode character. [OK] 🔥a-b 0 Non ASCII and ASCII characters. Full test results in `/_build/_tests/'. Test Successful in s. 2 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/unicode_testname.ml000066400000000000000000000005661437617477400241640ustar00rootroot00000000000000let () = Alcotest.run "Suite name containing file separators / and non-ASCII characters 🔥" [ ( "🔥", [ Alcotest.test_case "Non ASCII unicode character" `Quick (fun () -> ()); ] ); ( "🔥a-b", [ Alcotest.test_case "Non ASCII and ASCII characters" `Quick (fun () -> ()); ] ); ] alcotest-1.7.0/test/e2e/alcotest/passing/verbose_newlines-js.expected000066400000000000000000000011651437617477400260060ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/verbose_newlines.ml'. This run has ID `'. Print inside alpha [OK] alpha 0 0 newlines. Print inside beta [OK] beta 0 1 newline. Print inside gamma Lorem ipsum dolor sit amet, consectetur adipiscing elit, nullam malesuada dictum tortor in venenatis. [OK] gamma 0 1 newline + long line. Print inside delta ASSERT Lorem ipsum dolor sit amet, consectetur adipiscing elit, nullam malesuada dictum tortor in venenatis. [OK] delta 0 1 newline + long check. Test Successful in s. 4 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/verbose_newlines.expected000066400000000000000000000011651437617477400253740ustar00rootroot00000000000000Testing `test/e2e/alcotest/passing/verbose_newlines.ml'. This run has ID `'. Print inside alpha [OK] alpha 0 0 newlines. Print inside beta [OK] beta 0 1 newline. Print inside gamma Lorem ipsum dolor sit amet, consectetur adipiscing elit, nullam malesuada dictum tortor in venenatis. [OK] gamma 0 1 newline + long line. Print inside delta ASSERT Lorem ipsum dolor sit amet, consectetur adipiscing elit, nullam malesuada dictum tortor in venenatis. [OK] delta 0 1 newline + long check. Test Successful in s. 4 tests run. alcotest-1.7.0/test/e2e/alcotest/passing/verbose_newlines.ml000066400000000000000000000026771437617477400242140ustar00rootroot00000000000000(** Reproduction for https://github.com/mirage/alcotest/issues/225, testing the interaction between `--verbose` and newlines in the test stdout. *) let () = Alcotest.run ~verbose:true __FILE__ [ ( "alpha", [ Alcotest.test_case "0 newlines" `Quick (fun () -> Format.printf "Print inside alpha"); ] ); ( "beta", (* Progressive result reporting is broken for this test, since the carriage return happens on the wrong line. *) [ Alcotest.test_case "1 newline" `Quick (fun () -> Format.printf "Print inside beta\n"); ] ); ( "gamma", [ (* Reporting is also broken here. Even worse, some of the test std.out is clipped by the eventual result of the test. *) Alcotest.test_case "1 newline + long line" `Quick (fun () -> Format.printf "Print inside gamma\n\ Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ nullam malesuada dictum tortor in venenatis."); ] ); ( "delta", [ Alcotest.test_case "1 newline + long check" `Quick (fun () -> Format.printf "Print inside delta\n"; Alcotest.(check unit) "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ nullam malesuada dictum tortor in venenatis." () ()); ] ); ] alcotest-1.7.0/test/e2e/alcotest/source_code_position/000077500000000000000000000000001437617477400230475ustar00rootroot00000000000000alcotest-1.7.0/test/e2e/alcotest/source_code_position/dune000066400000000000000000000033571437617477400237350ustar00rootroot00000000000000(env (_ (env-vars (ALCOTEST_COLOR never) (ALCOTEST_SHOW_ERRORS true)))) (executable (name test_source_code_position) (libraries alcotest astring)) (rule (target with-position.actual) (action (setenv ALCOTEST_SOURCE_CODE_POSITION true (with-accepted-exit-codes (or 1 2 124 125) (with-outputs-to %{target} (run %{dep:test_source_code_position.exe})))))) (rule (target without-position.actual) (action (setenv ALCOTEST_SOURCE_CODE_POSITION false (with-accepted-exit-codes (or 1 2 124 125) (with-outputs-to %{target} (run %{dep:test_source_code_position.exe})))))) (rule (target pre-4.12.actual) (action (setenv ALCOTEST_SOURCE_CODE_POSITION true (with-accepted-exit-codes (or 1 2 124 125) (with-outputs-to %{target} (run %{dep:test_source_code_position.exe})))))) (rule (target with-position.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:with-position.actual})))) (rule (target without-position.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:without-position.actual})))) (rule (target pre-4.12.processed) (action (with-outputs-to %{target} (run ../../strip_randomness.exe %{dep:pre-4.12.actual})))) (rule (alias runtest) (package alcotest) (enabled_if (>= %{ocaml_version} 4.12.0)) (action (diff with-position.expected with-position.processed))) (rule (alias runtest) (package alcotest) (enabled_if (>= %{ocaml_version} 4.12.0)) (action (diff without-position.expected without-position.processed))) (rule (alias runtest) (package alcotest) (enabled_if (< %{ocaml_version} 4.12.0)) (action (diff pre-4.12.expected pre-4.12.processed))) alcotest-1.7.0/test/e2e/alcotest/source_code_position/pre-4.12.expected000066400000000000000000000070151437617477400257450ustar00rootroot00000000000000Testing `First suite'. This run has ID `'. [FAIL] to_test 0 capitalise. [FAIL] to_test 1 double all. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] to_test 0 capitalise. │ └──────────────────────────────────────────────────────────────────────────────┘ ASSERT strings FAIL strings Expected: `"A"' Received: `"B"' Logs saved to `/_build/_tests//to_test.000.output'. ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] to_test 1 double all. │ └──────────────────────────────────────────────────────────────────────────────┘ ASSERT int lists 1 FAIL int lists 1 Expected: `[1]' Received: `[2; 2; 4; 6]' Logs saved to `/_build/_tests//to_test.001.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 2 failures! in s. 2 tests run.Forging ahead regardless! Testing `Second suite'. This run has ID `'. [OK] Ωèone 0 Passing test 1. [FAIL] Ωèone 1 Failing test. [OK] Ωèone 2 Passing test 2. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] Ωèone 1 Failing test. │ └──────────────────────────────────────────────────────────────────────────────┘ ASSERT This was never going to work... FAIL This was never going to work... Logs saved to `/_build/_tests//U+03A9U+00E8one.001.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 3 tests run. Fatal error: exception Alcotest_engine__Core.Make(P)(M).Test_error alcotest-1.7.0/test/e2e/alcotest/source_code_position/test_source_code_position.ml000066400000000000000000000037141437617477400306630ustar00rootroot00000000000000(* * Copyright (c) 2022 Antonin Décimo * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) (* A module with functions to test *) module To_test = struct let capitalise = Astring.String.Ascii.uppercase let double_all = List.map (fun a -> a + a) end let test_capitalise () = To_test.capitalise "b" |> Alcotest.(check string) "strings" "A"; () let test_double_all () = To_test.double_all [ 1; 1; 2; 3 ] |> Alcotest.(check (list int)) "int lists 1" [ 1 ]; To_test.double_all [ 1; 1; 2; 3 ] |> Alcotest.(check (list int)) "int lists 2" [ 2 ] let suite1 = [ ( "to_test", [ ("capitalise", `Quick, test_capitalise); ("double all", `Slow, test_double_all); ] ); ] let suite2 = [ ( "Ωèone", [ ("Passing test 1", `Quick, fun () -> ()); ( "Failing test", `Quick, fun () -> Alcotest.fail "This was never going to work..." ); ("Passing test 2", `Quick, fun () -> ()); ] ); ] (* Run both suites completely, even if the first contains failures *) let () = try Alcotest.run ~and_exit:false "First suite" suite1 with Alcotest.Test_error -> Printf.printf "Forging ahead regardless!\n%!"; Alcotest.run ~and_exit:false "Second suite" suite2; Printf.printf "Finally done." alcotest-1.7.0/test/e2e/alcotest/source_code_position/with-position.expected000066400000000000000000000073211437617477400274120ustar00rootroot00000000000000Testing `First suite'. This run has ID `'. [FAIL] to_test 0 capitalise. [FAIL] to_test 1 double all. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] to_test 0 capitalise. │ └──────────────────────────────────────────────────────────────────────────────┘ ASSERT strings File "test/e2e/alcotest/source_code_position/test_source_code_position.ml", line 24, character 2: FAIL strings Expected: `"A"' Received: `"B"' Logs saved to `/_build/_tests//to_test.000.output'. ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] to_test 1 double all. │ └──────────────────────────────────────────────────────────────────────────────┘ ASSERT int lists 1 File "test/e2e/alcotest/source_code_position/test_source_code_position.ml", line 28, character 2: FAIL int lists 1 Expected: `[1]' Received: `[2; 2; 4; 6]' Logs saved to `/_build/_tests//to_test.001.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 2 failures! in s. 2 tests run.Forging ahead regardless! Testing `Second suite'. This run has ID `'. [OK] Ωèone 0 Passing test 1. [FAIL] Ωèone 1 Failing test. [OK] Ωèone 2 Passing test 2. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] Ωèone 1 Failing test. │ └──────────────────────────────────────────────────────────────────────────────┘ ASSERT This was never going to work... FAIL This was never going to work... Logs saved to `/_build/_tests//U+03A9U+00E8one.001.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 3 tests run. Fatal error: exception Alcotest_engine__Core.Make(P)(M).Test_error alcotest-1.7.0/test/e2e/alcotest/source_code_position/without-position.expected000066400000000000000000000070151437617477400301420ustar00rootroot00000000000000Testing `First suite'. This run has ID `'. [FAIL] to_test 0 capitalise. [FAIL] to_test 1 double all. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] to_test 0 capitalise. │ └──────────────────────────────────────────────────────────────────────────────┘ ASSERT strings FAIL strings Expected: `"A"' Received: `"B"' Logs saved to `/_build/_tests//to_test.000.output'. ────────────────────────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] to_test 1 double all. │ └──────────────────────────────────────────────────────────────────────────────┘ ASSERT int lists 1 FAIL int lists 1 Expected: `[1]' Received: `[2; 2; 4; 6]' Logs saved to `/_build/_tests//to_test.001.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 2 failures! in s. 2 tests run.Forging ahead regardless! Testing `Second suite'. This run has ID `'. [OK] Ωèone 0 Passing test 1. [FAIL] Ωèone 1 Failing test. [OK] Ωèone 2 Passing test 2. ┌──────────────────────────────────────────────────────────────────────────────┐ │ [FAIL] Ωèone 1 Failing test. │ └──────────────────────────────────────────────────────────────────────────────┘ ASSERT This was never going to work... FAIL This was never going to work... Logs saved to `/_build/_tests//U+03A9U+00E8one.001.output'. ────────────────────────────────────────────────────────────────────────────── Full test results in `/_build/_tests/'. 1 failure! in s. 3 tests run. Fatal error: exception Alcotest_engine__Core.Make(P)(M).Test_error alcotest-1.7.0/test/e2e/dune000066400000000000000000000005601437617477400156720ustar00rootroot00000000000000(env (_ (env-vars ; Don't run tests as if Alcotest was run in CI (CI false) ; Don't guess source code position for compat with < 4.12. (ALCOTEST_SOURCE_CODE_POSITION false)))) (executable (name gen_dune_rules) (libraries cmdliner fmt) (modules gen_dune_rules)) (executable (name strip_randomness) (libraries unix re) (modules strip_randomness)) alcotest-1.7.0/test/e2e/gen_dune_rules.ml000066400000000000000000000104711437617477400203460ustar00rootroot00000000000000(* Copied from Filename (stdlib) for pre-4.04 compatibility *) let chop_extension name = let is_dir_sep s i = match Sys.os_type with | "Unix" -> s.[i] = '/' | "Win32" | "Cygwin" -> let c = s.[i] in c = '/' || c = '\\' || c = ':' | _ -> assert false in let rec search_dot i = if i < 0 || is_dir_sep name i then invalid_arg "Filename.chop_extension" else if name.[i] = '.' then String.sub name 0 i else search_dot (i - 1) in search_dot (String.length name - 1) (* Read a file-path to a list of strings corresponding to lines in the file. *) let read_file filename = let lines = ref [] in let chan = open_in filename in try while true do lines := input_line chan :: !lines done; assert false with End_of_file -> close_in chan; List.rev !lines (* A test executable [foo.ml] may require certain options to be passed as specified in a [foo.opts] file. *) let options_of_test_file file = let options_file = chop_extension file ^ ".opts" in if not (Sys.file_exists options_file) then [] else read_file options_file let global_stanza ~libraries ~js filenames = let bases = List.map chop_extension filenames in let libraries = List.map (( ^ ) " ") libraries in let pp_sexp_list = Fmt.(list ~sep:(const string "\n ")) in Fmt.pr {|(executables (names %a ) (libraries alcotest alcotest.stdlib_ext%a) %s (modules %a ) ) |} (pp_sexp_list Fmt.string) bases Fmt.(list string) libraries (if js then "(modes exe js)" else "(modes exe)") (pp_sexp_list Fmt.string) bases let example_rule_stanza ~js ~expect_failure filename = let with_suffix x = if js then x ^ "-js" else x in let base = chop_extension filename in let options = options_of_test_file filename |> List.map (( ^ ) " ") in let accepted_exit_codes = Fmt.str "(or %s %d %d)" (if expect_failure then (* 1 = failing test, 2 = failed assertion outside runner *) "1 2" else "0") Cmdliner.Cmd.Exit.cli_error Cmdliner.Cmd.Exit.internal_error in (* Run Alcotest to get *.actual, then pass through the strip_randomness sanitiser to get *.processed. *) Fmt.pr {| (rule (target %s.actual) (action (with-outputs-to %%{target} (with-accepted-exit-codes %s (run %s%a))))) |} (with_suffix base) accepted_exit_codes (if js then Printf.sprintf "node %%{dep:%s.bc.js}" base else Printf.sprintf "%%{dep:%s.exe}" base) Fmt.(list string) options; Fmt.pr {| (rule (target %s.processed) (action (with-outputs-to %%{target} (run ../../strip_randomness.exe %%{dep:%s.actual})))) |} (with_suffix base) (with_suffix base) let example_alias_stanza ~js ~package filename = let with_suffix x = if js then x ^ "-js" else x in let base = chop_extension filename in Fmt.pr {| (rule (alias %s) (package %s) (action (diff %s.expected %s.processed))) |} (with_suffix "runtest") package (with_suffix base) (with_suffix base) let is_example filename = Filename.check_suffix filename ".ml" let main package expect_failure libraries js = Sys.readdir "." |> Array.to_list |> List.sort String.compare |> List.filter is_example |> function | [] -> () (* no tests to execute *) | tests -> global_stanza ~libraries ~js tests; List.iter (fun test -> example_rule_stanza ~js:false ~expect_failure test; example_alias_stanza ~js:false ~package test; if js then ( example_rule_stanza ~js:true ~expect_failure test; example_alias_stanza ~js:true ~package test)) tests open Cmdliner let package = let doc = Arg.info ~doc:"Package with which to associate the executables" [ "package" ] in Arg.(required & opt (some string) None & doc) let libraries = let doc = Arg.info ~doc:"Additional libraries to make available to the executable" [ "libraries" ] in Arg.(value & opt (list string) [] & doc) let js = let doc = Arg.info ~doc:"Test in javascript" [ "js" ] in Arg.(value & flag doc) let expect_failure = let doc = Arg.info ~doc:"Negate the return status of the tests" [ "expect-failure" ] in Arg.(value & flag doc) let cmd = let info = Cmd.info ~version:"%%VERSION%%" "gen_dune_rules" in Cmd.v info Term.(const main $ package $ expect_failure $ libraries $ js) let () = exit @@ Cmd.eval cmd alcotest-1.7.0/test/e2e/strip_randomness.ml000066400000000000000000000100621437617477400207360ustar00rootroot00000000000000let ( ^^ ) a b = Re.seq [ a; b ] let standardise_filesep = let re = Re.compile (Re.str Filename.dir_sep) in Re.replace_string ~all:true re ~by:"/" let build_context_replace = let open Re in let lterm, rterm = (* Contexts in which directories are printed (tests, manpage output etc.). *) ( group (alt [ char '`'; str "(absent=" ]), group (alt [ char '\''; char ')' ]) ) in let dir_sep = alt [ str Filename.dir_sep; char '/' ] in let t = seq [ lterm; rep any; str "_build"; dir_sep; str "_tests"; opt dir_sep; group (rep (diff any (set "\\/"))) (* : May be a UUID or a suite name (symlink), depending on whether or not we're running on Windows *); group (opt (seq [ dir_sep; rep any ])); rterm; ] in let re = compile t in fun s -> replace ~all:true re s ~f:(fun g -> let test_dir_opt = if Group.get g 2 = "" then "" else "" in let test_name = standardise_filesep (Group.get g 3) in Group.get g 1 ^ "/_build/_tests/" ^ test_dir_opt ^ test_name ^ Group.get g 4) let uuid_replace = let open Re in let t = seq [ str "ID `"; repn (alt [ rg 'A' 'Z'; digit ]) 8 (Some 8); char '\'' ] in let re = compile t in fun s -> replace_string ~all:true re ~by:"ID `'" s let time_replace = let open Re in let float = rep1 digit ^^ char '.' ^^ rep1 digit in let t = alt [ group (alt [ (* Maybe ANSI escape, depending on [--color] *) opt cntrl ^^ str "Test Successful" ^^ opt cntrl ^^ str " in "; rep1 digit ^^ str " failure! in "; rep1 digit ^^ str " failures! in "; str "\"time\": "; ]) ^^ float ^^ group (opt (char 's')); ] in let re = compile t in fun s -> replace re s ~f:(fun g -> Group.get g 1 ^ "" ^ Group.get g 2) let stacktrace_replace = let open Re in let stack_trace_line verb = str verb ^^ opt (rep1 print ^^ str " in ") (* exception name *) ^^ str "file \"" ^^ rep1 print ^^ str "\"" ^^ opt (str " (inlined)") ^^ str ", line " ^^ rep1 digit ^^ str ", characters " ^^ rep1 digit ^^ str "-" ^^ rep1 digit |> compile in let raised_at = stack_trace_line "Raised at " and called_from = stack_trace_line "Called from " in let called_from_unknown = str "Called from unknown location" |> compile in fun s -> match execp called_from s with | true -> (* The number of "Called from ..." lines is compiler-dependent, so we remove them all. *) `None | false -> ( match execp called_from_unknown s with | true -> `Unknown (replace_string ~all:true called_from_unknown s ~by:"") | false -> `Some (replace_string ~all:true raised_at s ~by:"")) let executable_name_normalization = let open Re in let t = alt [ str ".exe"; str ".bc.js" ] in let re = compile t in replace_string ~all:true re ~by:"." (* Remove all non-deterministic output in a given Alcotest log and write the result to std.out *) let () = let in_channel = open_in Sys.argv.(1) in let ( >>| ) x f = match x with None -> None | Some x -> Some (f x) in let ( >>= ) x f = match x with None -> `None | Some x -> f x in try let rec loop unknown_last = let sanitized_line = Some (input_line in_channel) >>| uuid_replace >>| build_context_replace >>| time_replace >>| executable_name_normalization >>= stacktrace_replace in let unknown_last = match sanitized_line with | `Some s -> Printf.printf "%s\n" s; false | `None -> false | `Unknown s -> if not unknown_last then Printf.printf "%s\n" s; true in loop unknown_last in loop false with End_of_file -> close_in in_channel