123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429(**************************************************************************)(* The OUnit library *)(* *)(* Copyright (C) 2002-2008 Maas-Maarten Zeeman. *)(* Copyright (C) 2010 OCamlCore SARL *)(* Copyright (C) 2013 Sylvain Le Gall *)(* *)(* The package OUnit is copyright by Maas-Maarten Zeeman, OCamlCore SARL *)(* and Sylvain Le Gall. *)(* *)(* Permission is hereby granted, free of charge, to any person obtaining *)(* a copy of this document and the OUnit software ("the Software"), to *)(* deal in the Software without restriction, including without limitation *)(* the rights to use, copy, modify, merge, publish, distribute, *)(* sublicense, and/or sell copies of the Software, and to permit persons *)(* to whom the Software is furnished to do so, subject to the following *)(* conditions: *)(* *)(* The above copyright notice and this permission notice shall be *)(* included in all copies or substantial portions of the Software. *)(* *)(* The Software is provided ``as is'', without warranty of any kind, *)(* express or implied, including but not limited to the warranties of *)(* merchantability, fitness for a particular purpose and noninfringement. *)(* In no event shall Maas-Maarten Zeeman 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. *)(* *)(* See LICENSE.txt for details. *)(**************************************************************************)openOUnitUtilsexceptionParse_errorofstringtypeconf=OUnitPropList.ttype'avar=conf->'atypemetadata={help:string;get_print:conf->string;parse_set:string->conf->unit;cli:conf->(string*Arg.spec*string)list;}letmetaconf=Hashtbl.create13letcheck_variable_namestr=let()=ifString.lengthstr=0thenfailwith"'' is not a valid name."inlet()=matchstr.[0]with|'0'..'9'|'_'->failwithf"%S is not a valid variable name. It must not start with %C."strstr.[0]|_->()inString.iter(function|'A'..'Z'|'a'..'z'|'_'|'0'..'9'->()|c->failwithf"%S is not a valid variable name. It must not contain %C."strc)strletcli_namename=letreplace_underscoresstr=letb=Buffer.create(String.lengthstr)inString.iter(function|'_'->Buffer.add_charb'-'|c->Buffer.add_charbc)str;Buffer.contentsbin"-"^replace_underscoresnameletsubstconfextra_subststr=letsubstitutions=Hashtbl.create(Hashtbl.lengthmetaconf)inlet()=(* Fill the substitutions table. *)Hashtbl.iter(funnamemetadata->Hashtbl.addsubstitutionsname(metadata.get_printconf))metaconf;List.iter(fun(k,v)->Hashtbl.addsubstitutionskv)extra_substinletbuff=Buffer.create(String.lengthstr)inBuffer.add_substitutebuff(funvar->tryHashtbl.findsubstitutionsvarwithNot_found->failwithf"Unknown substitution variable %S in %S."varstr)str;Buffer.contentsbuffletmake~name~parse~print~default~help~fcli()=let()=check_variable_namename;ifHashtbl.memmetaconfnamethenfailwithf"Duplicate definition for configuration variable %S."nameinletset,get=OUnitPropList.new_propertydefaultinletparse_setstrconf=setconf(parsestr)inletget_printconf=print(getconf)inHashtbl.addmetaconfname{help=help;get_print=get_print;parse_set=parse_set;cli=(funconf->fcli(getconf)(setconf))};(get:'avar)letmake_stringnamedefaulthelp=make~name~parse:(funs->s)~print:(funs->s)~default~help~fcli:(fun_set->[cli_namename,Arg.Stringset,"str "^help])()letmake_string_substnamedefaulthelp=letget=make_stringnamedefaulthelpin(fun?(extra_subst=[])conf->substconfextra_subst(getconf))letmake_string_optnamedefaulthelp=make~name~parse:(function|"none"->None|str->Somestr)~print:(function|Somex->x|None->"none")~default~help~fcli:(fun_set->[cli_namename,Arg.String(funstr->set(Somestr)),"str "^help;cli_name("no_"^name),Arg.Unit(fun()->setNone),Printf.sprintf" Reset value of %s."name])()letmake_string_subst_optnamedefaultopt=letget=make_string_optnamedefaultoptin(fun?(extra_subst=[])conf->matchgetconfwith|Somestr->Some(substconfextra_subststr)|None->None)letmake_intnamedefaulthelp=make~name~parse:(funstr->tryint_of_stringstrwithFailure_->raise(Parse_error(Printf.sprintf"%S is not an integer."str)))~print:string_of_int~default~help~fcli:(fun_set->[cli_namename,Arg.Intset,"i "^help])()letmake_floatnamedefaulthelp=make~name~parse:(funstr->tryfloat_of_stringstrwithFailure_->raise(Parse_error(Printf.sprintf"%S is not a float."str)))~print:string_of_float~default~help~fcli:(fun_set->[cli_namename,Arg.Floatset,"f "^help])()letmake_boolnamedefaulthelp=make~name~parse:(funstr->trybool_of_stringstrwithFailure_->raise(Parse_error(Printf.sprintf"%S is not a boolean (true or false)."str)))~print:string_of_bool~default~help~fcli:(fun_set->[cli_namename,Arg.Boolset,"{true|false} "^help])()letmake_enumnameget_enumsdefaulthelp=letparsestr=letenum_lst=get_enums()inifnot(List.exists(fun(str',_)->str=str')enum_lst)thenraise(Parse_error(Printf.sprintf"%S is not an allowed value for %s."strname));strinletget=make~name~parse~print:(funs->s)~default~help~fcli:(fun_set->[cli_namename,Arg.Symbol(List.mapfst(get_enums()),set)," "^help])()infunconf->trygetconf,List.assoc(getconf)(get_enums())withNot_found->failwithf"Enums list for %s has changed during execution."nameletmake_execname=letdefault=letpwd=Sys.getcwd()inletbn=Filename.concatpwdnameinifSys.file_exists(bn^".native")thenbn^".native"elseifSys.file_exists(bn^".byte")thenbn^".byte"elsenameinmake_stringnamedefault(Printf.sprintf"Executable %s."name)letset~originconfnamevalue=try(Hashtbl.findmetaconfname).parse_setvalueconfwith|Not_found->failwithf"Variable %S is not defined in the application.\n%s"nameorigin|Parse_errorstr->failwith(str^"\n"^origin)letfile_parseconffn=letparselinenoline=letorigin=Printf.sprintf"File \"%s\", line %d."fnlinenoinmatchtrim(trim_commentline)with|""->()|str->beginletname,value=tryScanf.sscanfstr"%s = %S"(funnamevalue->name,value)withScanf.Scan_failure_->begintryScanf.sscanfstr"%s = %s"(funnamevalue->name,value)withScanf.Scan_failure_->failwithf"Unparsable line: %s\n%s"lineoriginendinset~originconfnamevalueendinletchn=open_infninletlineno=ref0intrywhiletruedoletline=input_linechninincrlineno;parse!linenolinedone;()with|End_of_file->close_inchn|e->close_inchn;raiseeletenv_parseconf=letparsename=letuppercase_name=letb=Buffer.create(String.lengthname)inString.iter(function|'a'..'z'asc->Buffer.add_charb(Char.chr((Char.codec)-32))|c->Buffer.add_charbc)name;Buffer.contentsbinletenv_name="OUNIT_"^uppercase_nameintryletvalue=Sys.getenvenv_namein(* Check and translate double quoted variable. *)letvalue=tryScanf.sscanfvalue"%S"(funs->s)withScanf.Scan_failure_->valueinletorigin=Printf.sprintf"Environment variable %s=%S."env_namevalueinset~originconfnamevaluewithNot_found->()inHashtbl.iter(funname_->parsename)metaconfletcli_parse?argvextra_specsconf=letspecs=Hashtbl.fold(fun_metadatalst->letcli_lst=matchmetadata.cliconfwith|(key,spec,doc)::tl->(key,spec,doc^(Printf.sprintf" (default: %s)"(metadata.get_printconf)))::tl|[]->[]incli_lst@lst)metaconf[]inletall_specs=Arg.align(["-conf",Arg.String(file_parseconf),"fn Read configuration file."]@(List.sortStdlib.comparespecs)@extra_specs)inletarg_parse=matchargvwith|Somearr->Arg.parse_argv~current:(ref0)arr|None->Arg.parseinarg_parseall_specs(funx->raise(Arg.Bad("Unexpected argument: "^x)))("usage: "^Sys.argv.(0)^" options*")letdefault?(preset=[])()=letconf=OUnitPropList.create()inList.iter(fun(name,value)->set~origin:"Preset by program."confnamevalue)preset;conf(** Load test options from file, environment and command line (in this order).
Not that [extra_specs] is here for historical reason, better use [make] to
create command line options.
*)letload?preset?argvextra_specs=letconf=default?preset()inifSys.file_exists"ounit.conf"thenfile_parseconf"ounit.conf";env_parseconf;cli_parse?argvextra_specsconf;confletdumpconf=Hashtbl.fold(funnamemetadatalst->(name,metadata.get_printconf)::lst)metaconf[]