Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file cli.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334(*
* Copyright (c) 2013-2016 Thomas Gazagnaire <thomas@gazagnaire.org>
* Copyright (c) 2019 Craig Ferguson <me@craigfe.io>
*
* 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.
*)openCmdlineropenAstringmoduleIntSet=Core.IntSetmoduletypeS=sigincludeCore.Svalrun:(?argv:stringarray->string->unittestlist->return)with_optionsvalrun_with_args:(?argv:stringarray->string->'aCmdliner.Term.t->'atestlist->return)with_optionsendmoduletypeMAKER=functor(P:Platform.MAKER)(M:Monad.S)->Swithtypereturn=unitM.tmoduleMake(P:Platform.MAKER)(M:Monad.S):Swithtypereturn=unitM.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}. *)moduleC=Core.Make(P)(M)includeCmoduleP=P(M)letset_colorcolor_flag=letstyle_renderer=matchcolor_flagwith|Some`Auto->None|Some(`Ansi_tty|`None)asa->a|None->(try(* Default to [always] when running inside Dune *)let(_:string)=Sys.getenv"INSIDE_DUNE"inSome`Ansi_ttywithNot_found->None)inP.setup_std_outputs?style_renderer()letset_color=letenv=Arg.env_var"ALCOTEST_COLOR"inletstyle_renderer=letenum=[("auto",`Auto);("always",`Ansi_tty);("never",`None)]inletcolor=Arg.enumenuminletenum_alts=Arg.doc_alts_enumenuminletdoc=strf"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")inArg.(value&opt(somecolor)None&info["color"]~env~doc~docv:"WHEN")inTerm.(constset_color$style_renderer)typeruntime_options={verbose:booloption;compact:booloption;tail_errors:[`Unlimited|`Limitofint]option;show_errors:booloption;quick_only:booloption;json:booloption;log_dir:stringoption;}(* Merge two ['a option]s with a left [Some] taking priority *)let(||*)ab=match(a,b)withSomea,_->Somea|None,b->bletv_runtime_flags~defaults(`Verboseverbose)(`Compactcompact)(`Tail_errorstail_errors)(`Show_errorsshow_errors)(`Quick_onlyquick_only)(`Jsonjson)(`Log_dirlog_dir)=letverbose=verbose||*defaults.verboseinletcompact=compact||*defaults.compactinletshow_errors=show_errors||*defaults.show_errorsinletquick_only=quick_only||*defaults.quick_onlyinletjson=json||*defaults.jsoninletlog_dir=Somelog_dirinlettail_errors=tail_errors||*defaults.tail_errorsin{verbose;compact;tail_errors;show_errors;quick_only;json;log_dir}letrun_test?and_exit{verbose;compact;tail_errors;show_errors;quick_only;json;log_dir}(`Test_filterfilter)()testsnameargs=run_with_args?and_exit?verbose?compact?tail_errors?quick_only?show_errors?json?filter?log_dirnametestsargsletfmapfx=Term.(app(constf)x)(* 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. *)letto_tristate=fmap(functiontrue->Sometrue|false->None)letjson=letdoc="Display JSON for the results, to be used by a script."inArg.(value&flag&info["json"]~docv:""~doc)|>to_tristate|>fmap(funx->`Jsonx)letlog_dir=letfname_concatl=List.fold_leftFilename.concat""linletdefault_dir=fname_concat[P.getcwd();"_build";"_tests"]inletdoc="Where to store the log files of the tests."inArg.(value&optdirdefault_dir&info["o"]~docv:"DIR"~doc)|>fmap(funx->`Log_dirx)letverbose=letenv=Arg.env_var"ALCOTEST_VERBOSE"inletdoc="Display the test outputs. $(b,WARNING:) when using this option the \
output logs will not be available for further inspection."inArg.(value&flag&info~env["v";"verbose"]~docv:""~doc)|>to_tristate|>fmap(funx->`Verbosex)letcompact=letenv=Arg.env_var"ALCOTEST_COMPACT"inletdoc="Compact the output of the tests"inArg.(value&flag&info~env["c";"compact"]~docv:""~doc)|>to_tristate|>fmap(funx->`Compactx)letlimit_parsers=matchswith|"unlimited"->Ok`Unlimited|s->(tryletn=int_of_stringsinifn<0thenError(`Msg"numeric limit must be nonnegative or 'unlimited'")elseOk(`Limitn)withFailure_->Error(`Msg"invalid numeric limit"))letlimit_printerppflimit=matchlimitwith|`Unlimited->Fmt.pfppf"unlimited"|`Limitn->Fmt.pfppf"%i"n(* Parse/print a nonnegative number of lines or "unlimited". *)letlimit=Cmdliner.Arg.conv(limit_parser,limit_printer)lettail_errors=letenv=Arg.env_var"ALCOTEST_TAIL_ERRORS"inletdoc="Show only the last $(docv) lines of output in case of an error."inArg.(value&opt(somelimit)None&info~env["tail-errors"]~docv:"N"~doc)|>fmap(funx->`Tail_errorsx)letshow_errors=letenv=Arg.env_var"ALCOTEST_SHOW_ERRORS"inletdoc="Display the test errors."inArg.(value&flag&info~env["e";"show-errors"]~docv:""~doc)|>to_tristate|>fmap(funx->`Show_errorsx)letquick_only=letenv=Arg.env_var"ALCOTEST_QUICK_TESTS"inletdoc="Run only the quick tests."inArg.(value&flag&info~env["q";"quick-tests"]~docv:""~doc)|>to_tristate|>fmap(funx->`Quick_onlyx)letflags_with_defaultsdefaults=Term.(pure(v_runtime_flags~defaults)$verbose$compact$tail_errors$show_errors$quick_only$json$log_dir)letregex=letparses=tryOkRe.(compile@@Pcre.res)with|Re.Perl.Parse_error->Error(`Msg"Perl-compatible regexp parse error")|Re.Perl.Not_supported->Error(`Msg"unsupported regexp feature")inletprint=Re.pp_reinArg.conv(parse,print)exceptionInvalid_formatletint_range_list:IntSet.tCmdliner.Arg.conv=letparses=letset=refIntSet.emptyinletacci=set:=IntSet.addi!setinletranges=String.cuts~sep:","sinletprocess_ranges=letbounds=String.cuts~sep:".."s|>List.mapString.to_intinmatchboundswith|[Somei]->acci|[Somelower;Someupper]whenlower<=upper->fori=lowertoupperdoaccidone|_->raiseInvalid_formatinmatchList.iterprocess_rangerangeswith|()->Ok!set|exceptionInvalid_format->Error(`Msg"must be a comma-separated list of integers / integer ranges")inletprintppfset=Fmt.(braces@@list~sep:commaint)ppf(IntSet.elementsset)inArg.conv(parse,print)lettest_filter=letname_regex=letdoc="A regular expression matching the names of tests to run"inArg.(value&pos0(someregex)None&info[]~doc~docv:"NAME_REGEX")inletnumber_filter=letdoc="A comma-separated list of test case numbers (and ranges of numbers) \
to run, e.g: '4,6-10,19'"inArg.(value&pos1(someint_range_list)None&info[]~doc~docv:"TESTCASES")inTerm.(pure(funnt->`Test_filter(Some(n,t)))$name_regex$number_filter)letdefault_cmd?and_exitruntime_flagsargslibrary_nametests=letexec_name=Filename.basenameSys.argv.(0)inletdoc="Run all the tests."inletflags=flags_with_defaultsruntime_flagsin(Term.(pure(run_test?and_exit)$flags$pure(`Test_filterNone)$set_color$args$purelibrary_name$puretests),Term.infoexec_name~doc)lettest_cmd?and_exitruntime_flags~filterargslibrary_nametests=letdoc="Run a subset of the tests."inletflags=flags_with_defaultsruntime_flagsinletfilter=Term.(pure(funa->matchawith`Test_filterNone->filter|_->a)$test_filter)in(Term.(pure(run_test?and_exit)$flags$filter$set_color$args$purelibrary_name$puretests),Term.info"test"~doc)letlist_cmdtests=letdoc="List all available tests."in(Term.(pure(fun()->list_tests)$set_color$puretests),Term.info"list"~doc)letrun_with_args?(and_exit=true)?verbose?compact?tail_errors?quick_only?show_errors?json?filter?log_dir?argvname(args:'aTerm.t)(tl:'atestlist)=let(>>=)=M.bindinletruntime_flags={verbose;compact;tail_errors;show_errors;quick_only;json;log_dir}inletchoices=[list_cmdtl;test_cmd~and_exitruntime_flags~filter:(`Test_filterfilter)argsnametl;]inletexit_or_returnresult=ifand_exitthenexit(Term.exit_status_of_resultresult)elseM.return()inletresult=Term.eval_choice?argv~catch:and_exit(* Only log exceptions not raised to the user code *)(default_cmd~and_exitruntime_flagsargsnametl)choicesinmatchresultwith|`Okunit_m->unit_m>>=fun()->exit_or_returnresult|`Help|`Version|`Error`Exn->exit_or_returnresult|`Error(`Parse|`Term)->exit(Term.exit_status_of_resultresult)letrun?and_exit?verbose?compact?tail_errors?quick_only?show_errors?json?filter?log_dir?argvnametl=run_with_args?and_exit?verbose?compact?tail_errors?quick_only?show_errors?json?filter?log_dir?argvname(Term.pure())tlend