123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246(*
* Copyright (c) 2013-2016 Thomas Gazagnaire <thomas@gazagnaire.org>
* Copyright (c) 2019 Craig Ferguson <craig@tarides.com>
*
* 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.
*)includePp_intfincludePp_intf.Typesopen!ImportopenModelletmap_thetat~fppf=f(funppf()->tppf)ppf()letpp_pluralppfx=Fmt.pfppf(ifx<2then""else"s")letcolour_of_tag=function|`Ok->`Green|`Fail->`Red|`Skip|`Todo|`Assert->`Yellowletstring_of_tag=function|`Ok->"OK"|`Fail->"FAIL"|`Skip->"SKIP"|`Todo->"TODO"|`Assert->"ASSERT"letpp_tag~wrappedppftyp=letcolour=colour_of_tagtypinlettag=string_of_tagtypinlettag=ifwrappedthen"["^tag^"]"elsetaginFmt.(styledcolourstring)ppftaglettag=pp_tag~wrapped:falsemoduleMake(P:sigvalstdout_columns:unit->intoptionend)=structincludeTypesletterminal_width=lazy(matchP.stdout_columns()withSomew->w|None->80)letrresult_errorppf=function|`Error(_,e)->Fmt.pfppf"%a@,"e()|`Exn(_,n,e)->Fmt.pfppf"[%s] @[<v>%a@]"ne()|`Ok|`Todo_|`Skip->()(* Colours *)letcolorcppffmt=Fmt.(styledcstring)ppffmtletred_sfmt=color`Redfmtletredppffmt=Fmt.kstr(funstr->red_sppfstr)fmtletgreen_sfmt=color`Greenfmtletyellow_sfmt=color`Yellowfmtletleft_gutter=2letleft_tag=14letleft_total=left_gutter+left_tagletleftnbppppfa=lets=Fmt.to_to_stringppainletnb=nb-String.length_utf8sinifnb<=0thenppppfaelse(ppppfa;Fmt.stringppf(String.v~len:nb(fun_->' ')))letpp_test_name~max_labelppftname=letname_len=Test_name.lengthtnameinletindex=Test_name.indextnameinletpadding=matchmax_label+8-name_lenwith|nwhenn<=0->""|n->String.v~len:n(fun_->' ')inFmt.pfppf"%a%s%3d"Fmt.(styled`CyanTest_name.pp)tnamepaddingindexletinfo?(available_width=Lazy.forceterminal_width)~max_label~doc_of_test_nameppftname=letpp_test_nameppf=Fmt.pfppf"%a "(pp_test_name~max_label)tnameinlettest_doc=lettest_doc=doc_of_test_nametnameinletavailable_width=pp_test_nameFormat.str_formatter;letused_width=String.length_utf8(Format.flush_str_formatter())inavailable_width-used_widthinifString.length_utf8test_doc<=available_widththentest_docelseString.prefix_utf8(available_width-3)test_doc^"..."inFmt.pfppf"%t%s"pp_test_nametest_doclettag_of_result=function|`Ok->`Ok|`Exn_|`Error_->`Fail|`Skip->`Skip|`Todo_->`Todoletpp_resultppfresult=lettag=tag_of_resultresultinleftleft_tag(pp_tag~wrapped:true)ppftagletpp_result_compactppfresult=letcolour=result|>tag_of_result|>colour_of_taginletchar=matchresultwith|`Ok->'.'|`Exn_|`Error_->'F'|`Skip->'S'|`Todo_->'T'inFmt.(styledcolourchar)ppfcharletleft_padding~with_selector=letopenFmtin(ifwith_selectorthenconst(styled`Bold(styled`Redchar))'>'elseconstchar' ')++constchar' 'letpp_result_full~max_label~doc_of_test_name~selector_on_failureppf(path,result)=letwith_selector=selector_on_failure&&Run_result.is_failureresultin(left_padding~with_selector)ppf();pp_resultppfresult;letavailable_width=Lazy.forceterminal_width-left_totalin(info~available_width~max_label~doc_of_test_name)ppfpath;()letevent_line~margins~max_label~doc_of_test_nameppf=function|`Result(p,r)->pp_resultppfr;(info~available_width:(Lazy.forceterminal_width-margins-left_total)~max_label~doc_of_test_name)ppfp|_->assertfalseletevent~isatty~compact~max_label~doc_of_test_name~selector_on_failure~tests_so_farppfevent=match(compact,isatty,event)with|true,_,`Start_|_,false,`Start_->()|false,true,`Starttname->Fmt.(left_padding~with_selector:false++const(leftleft_tagyellow_s)"..."++const(info~available_width:(Lazy.forceterminal_width-left_total)~max_label~doc_of_test_name)tname)ppf()|true,_,`Result(_,r)->pp_result_compactppfr;(* Wrap compact output to terminal width manually *)if(tests_so_far+1)modLazy.forceterminal_width=0thenFormat.pp_force_newlineppf();()|false,_,`Result(tname,r)->ifisattythenFmt.pfppf"\r";Fmt.pfppf"%a@,"(pp_result_full~max_label~doc_of_test_name~selector_on_failure)(tname,r)letpp_suite_errors~show_all=function|[]->Fmt.nop|x::_asxs->(ifshow_allthenxselse[x])|>Fmt.concatletquotedf=Fmt.(constchar'`'++f++constchar'\'')letwith_surrounding_box(typea)(f:aFmt.t):aFmt.t=funppfa->(* 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. *)lettrue_width=Fmt.kstrString.length_utf8"| %a |"fainletmin_width=Lazy.forceterminal_widthinletwidth=maxmin_widthtrue_widthinletright_padding=String.v~len:(width-true_width)(fun_->' ')inlets=Fmt.(const(styled`Faintstring))inletbars=List.init(width-2)(fun_->"─")|>String.concatinlettop=s("┌"^bars^"┐")andmid=Fmt.(s"│ "++f++s(right_padding^" │"))andbottom=s("└"^bars^"┘")inFmt.(top++cut++mid++cut++bottom++cut)ppfalethorizontal_rule(typea)ppf(_:a)=letopenFmtin(conststring" "++const(styled`Faintstring)(List.init(Lazy.forceterminal_width-2)(fun_->"─")|>String.concat)++cut)ppf()letpp_full_logsppflog_dir=Fmt.pfppf"Full test results in %t.@,"(map_theta~f:Fmt.(styled`Cyan>>quoted)log_dir)letpp_summaryppfr=letpp_failuresppf=function|0->green_sppf"Test Successful"|n->redppf"%d failure%a!"npp_pluralninFmt.pfppf"%a in %.3fs. %d test%a run.@,"pp_failuresr.failuresr.timer.successpp_pluralr.successletsuite_results~log_dirconfigppfr=letprint_summary=(notconfig#compact)||r.failures>0inmatchconfig#jsonwith|true->(* Return the json for the api, dirty out, to avoid new dependencies *)Fmt.pfppf{|{
"success": %i,
"failures": %i,
"time": %f
}
|}r.successr.failuresr.time|false->Format.pp_force_newlineppf();Format.pp_open_vboxppf0;ifconfig#compactthenFmt.cutppf();(pp_suite_errors~show_all:(config#verbose||config#show_errors)r.errors)ppf();ifprint_summarythen(ifnotconfig#verbosethenpp_full_logsppflog_dir;pp_summaryppfr);Format.pp_close_boxppf()letuser_errormsg=Formatters.epr"%a: %s\n"Fmt.(styled`Redstring)"ERROR"msg;exit1end