123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271(*
* Copyright (c) 2013-2016 Thomas Gazagnaire <thomas@gazagnaire.org>
*
* 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.
*)moduletypeTESTABLE=sigtypetvalpp:tFmt.tvalequal:t->t->boolendtype'atestable=(moduleTESTABLEwithtypet='a)letpp(typea)(t:atestable)=let(moduleT)=tinT.ppletequal(typea)(t:atestable)=let(moduleT)=tinT.equalletisnanf=FP_nan=classify_floatflettestable(typea)(pp:aFmt.t)(equal:a->a->bool):atestable=letmoduleM=structtypet=aletpp=ppletequal=equalendin(moduleM)letmapft=letppppf=(Fmt.usingf(ppt))ppfandequalab=equalt(fa)(fb)intestableppequalletint32=testableFmt.int32(=)letint64=testableFmt.int64(=)letint=testableFmt.int(=)letfloateps=letsamexy=(isnanx&&isnany)(* compare infinities *)||x=y||abs_float(x-.y)<=epsintestableFmt.floatsameletchar=letpp_charppfx=Fmt.pfppf"%C"xintestablepp_char(=)letstring=letpp_stringppfx=Fmt.pfppf"%S"xintestablepp_string(=)letbytes=testable(funfmtbytes->Fmt.fmt"%S"fmt(Bytes.to_stringbytes))(=)letbool=testableFmt.bool(=)letunit=testable(Fmt.any"()")(=)letliste=letreceql1l2=match(l1,l2)with|x::xs,y::ys->equalexy&&eqxsys|[],[]->true|_->falseintestable(Fmt.Dump.list(ppe))eqletseqe=letreceqs1s2=match(s1(),s2())with|Seq.Cons(x,xs),Seq.Cons(y,ys)->equalexy&&eqxsys|Nil,Nil->true|_->falseintestable(Fmt.Dump.seq(ppe))eqletslist(typea)(a:atestable)compare=letl=listainleteql1l2=equall(List.sortcomparel1)(List.sortcomparel2)intestable(ppl)eqletarraye=leteqa1a2=letm,n=Array.(lengtha1,lengtha2)inletrecgoi=i=m||(equalea1.(i)a2.(i)&&go(i+1))inm=n&&go0intestable(Fmt.Dump.array(ppe))eqletpairab=leteq(a1,b1)(a2,b2)=equalaa1a2&&equalbb1b2intestable(Fmt.Dump.pair(ppa)(ppb))eqlettripleabc=leteq(a1,b1,c1)(a2,b2,c2)=equalaa1a2&&equalbb1b2&&equalcc1c2inletpp=Fmt.(parens(using(fun(x,_,_)->x)(box(ppa))++comma++using(fun(_,x,_)->x)(box(ppb))++comma++using(fun(_,_,x)->x)(box(ppc))))intestableppeqletoptione=leteqxy=match(x,y)with|Somea,Someb->equaleab|None,None->true|_->falseintestable(Fmt.Dump.option(ppe))eqletresultae=leteqxy=match(x,y)with|Okx,Oky->equalaxy|Errorx,Errory->equalexy|_->falseintestable(Fmt.Dump.result~ok:(ppa)~error:(ppe))eqletof_pppp=testablepp(=)letpass(typea)=letmoduleM=structtypet=aletppfmt_=Fmt.stringfmt"Alcotest.pass"letequal__=trueendin(moduleM:TESTABLEwithtypet=M.t)letreject(typea)=letmoduleM=structtypet=aletppfmt_=Fmt.stringfmt"Alcotest.reject"letequal__=falseendin(moduleM:TESTABLEwithtypet=M.t)letshow_assert=function|""->()|msg->Fmt.(flush(Formatters.get_stdout():>Format.formatter))()(* Flush any test stdout preceding the assert *);Format.fprintf(Formatters.get_stderr():>Format.formatter)"%a %s\n%!"Pp.tag`Assertmsgletcheck_errfmt=raise(Core.Check_errorfmt)moduleSource_code_position=structtypehere=Lexing.positiontypepos=string*int*int*intendtype'aextra_info=?here:Source_code_position.here->?pos:Source_code_position.pos->'aletpp_location=letpp=Fmt.styled`Bold(funppf(f,l,c)->Fmt.pfppf"File \"%s\", line %d, character %d:@,"flc)infun?here?posppf->match(here,pos)with|Some(here:Source_code_position.here),_->ppppf(here.pos_fname,here.pos_lnum,here.pos_cnum-here.pos_bol)|_,Some(fname,lnum,cnum,_)->ppppf(fname,lnum,cnum)|None,None->()letcheck(typea)?here?pos(t:atestable)msg(expected:a)(actual:a)=show_assertmsg;ifnot(equaltexpectedactual)thenletopenFmtinlets=conststringinletpp_error=matchmsgwith|""->nop|_->constPp.tag`Fail++s(" "^msg)++cutandpp_expectedppf()=Fmt.pfppf" Expected: `%a'"(styled`Green(ppt))expected;Format.pp_print_if_newlineppf();Fmt.cutppf();()andpp_actualppf()=Fmt.pfppf" Received: `%a'"(styled`Red(ppt))actualandhere,pos=match(here,pos)with|None,None->(Callsite_loc.get(),None)|_->(here,pos)inraise(Core.Check_errorFmt.(vbox((funppf()->pp_location?here?posppf)++pp_error++cut++pp_expected++cut++pp_actual)++cut))letcheck'?here?post~msg~expected~actual=check?here?postmsgexpectedactualletfail?here?posmsg=show_assertmsg;check_err(funppf()->Fmt.pfppf"%t%a %s"(pp_location?here?pos)Pp.tag`Failmsg)letfailf?here?posfmt=Fmt.kstr(funmsg->fail?here?posmsg)fmtletnegt=testable(ppt)(funxy->not(equaltxy))letcollect_exceptionf=tryf();Nonewithe->Someeletcheck_raises?here?posmsgexnf=show_assertmsg;matchcollect_exceptionfwith|None->check_err(funppf()->Fmt.pfppf"%t%a %s: expecting %a, got nothing."(pp_location?here?pos)Pp.tag`FailmsgFmt.exnexn)|Somee->ife<>exnthencheck_err(funppf()->Fmt.pfppf"%t%a %s: expecting %a, got %a."(pp_location?here?pos)Pp.tag`FailmsgFmt.exnexnFmt.exne)letmatch_raises?here?posmsgexnpf=show_assertmsg;matchcollect_exceptionfwith|None->check_err(funppf()->Fmt.pfppf"%t%a %s: got nothing."(pp_location?here?pos)Pp.tag`Failmsg)|Somee->ifnot(exnpe)thencheck_err(funppf()->Fmt.pfppf"%t%a %s: got %a."(pp_location?here?pos)Pp.tag`FailmsgFmt.exne)letskip()=raiseCore.V1.Skip