123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411(**************************************************************************)(* 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. *)(**************************************************************************)openOUnitUtilsexceptionSkipofstringexceptionTodoofstringexceptionOUnit_failureofstring(** See OUnit.mli. *)typenode=ListItemofint|Labelofstring(** See OUnit.mli. *)typepath=nodelist(** See OUnit2.mli. *)typebacktrace=stringoption(* The type of length of a test. *)typetest_length=|Immediate(* < 1s *)|Short(* < 1min *)|Long(* < 10min *)|Huge(* < 30min *)|Custom_lengthoffloat(** See OUnit.mli. *)typeresult=|RSuccess|RFailureofstring*OUnitLogger.positionoption*backtrace|RErrorofstring*backtrace|RSkipofstring|RTodoofstring|RTimeoutoftest_length(* See OUnit.mli. *)typeresult_full=(path*result*OUnitLogger.positionoption)typeresult_list=result_fulllisttypelog_event_t=(path,result)OUnitLogger.log_event_ttypelogger=(path,result)OUnitLogger.loggertypectxt=(* TODO: hide this to avoid building a context outside. *){conf:OUnitConf.conf;logger:logger;shared:OUnitShared.shared;path:path;test_logger:resultOUnitLogger.Test.t;(* TODO: Still a race condition possible, what if another threads
* modify anything during the process (e.g. register tear down).
*)mutabletear_down:(ctxt->unit)list;tear_down_mutex:OUnitShared.Mutex.t;non_fatal:result_fulllistref;non_fatal_mutex:OUnitShared.Mutex.t;initial_environment:stringarray;}typetest_fun=ctxt->unit(* The type of tests. *)typetest=|TestCaseoftest_length*test_fun|TestListoftestlist|TestLabelofstring*testletdelay_of_length=function|Immediate->20.0(* 20 seconds *)|Short->600.0(* 10 minutes *)|Long->1800.0(* 30 minutes *)|Huge->3600.0(* 1 hour *)|Custom_lengthf->fletget_shard_idtest_ctxt=test_ctxt.logger.OUnitLogger.lshard(** Isolate a function inside a context. All the added tear down will run before
returning.
*)letsection_ctxtctxtf=letold_tear_down=OUnitShared.Mutex.with_lockctxt.sharedctxt.tear_down_mutex(fun()->ctxt.tear_down)inletclean_exit()=OUnitShared.Mutex.with_lockctxt.sharedctxt.tear_down_mutex(fun()->List.iter(funtear_down->tear_downctxt)ctxt.tear_down;ctxt.tear_down<-old_tear_down)inOUnitShared.Mutex.with_lockctxt.sharedctxt.tear_down_mutex(fun()->ctxt.tear_down<-[]);tryletres=fctxtinclean_exit();reswithe->clean_exit();raisee(** Create a context and run the function. *)letwith_ctxtconfloggersharednon_fataltest_pathf=letctxt={conf=conf;logger=logger;path=test_path;shared=shared;test_logger=OUnitLogger.Test.createloggertest_path;tear_down=[];tear_down_mutex=OUnitShared.Mutex.createOUnitShared.ScopeProcess;non_fatal=non_fatal;non_fatal_mutex=OUnitShared.Mutex.createOUnitShared.ScopeProcess;initial_environment=Unix.environment();}insection_ctxtctxtfletstandard_modules=["arg.ml";"arrayLabels.ml";"array.ml";"buffer.ml";"callback.ml";"camlinternalLazy.ml";"camlinternalMod.ml";"camlinternalOO.ml";"char.ml";"complex.ml";"digest.ml";"filename.ml";"format.ml";"gc.ml";"genlex.ml";"hashtbl.ml";"int32.ml";"int64.ml";"lazy.ml";"lexing.ml";"listLabels.ml";"list.ml";"map.ml";"marshal.ml";"moreLabels.ml";"nativeint.ml";"obj.ml";"oo.ml";"parsing.ml";"pervasives.ml";"printexc.ml";"printf.ml";"queue.ml";"random.ml";"scanf.ml";"set.ml";"sort.ml";"stack.ml";"std_exit.ml";"stdLabels.ml";"stream.ml";"stringLabels.ml";"string.ml";"sys.ml";"weak.ml";"unix.ml";](** Transform an exception in a result. *)letresult_full_of_exceptionctxte=letbacktrace()=ifPrintexc.backtrace_status()thenSome(Printexc.get_backtrace())elseNoneinletlocate_exn()=ifPrintexc.backtrace_status()thenbeginletlst=extract_backtrace_position(Printexc.get_backtrace())inletpos_opt=tryList.find(function|None->false|Some(fn,_)->not(starts_with~prefix:"oUnit"(Filename.basenamefn))&¬(List.memfnstandard_modules))lstwithNot_found->Noneinmatchpos_optwith|Some(filename,line)->Some{OUnitLogger.filename=filename;line=line}|None->NoneendelseNoneinletresult=matchewith|OUnit_failures->RFailure(s,locate_exn(),backtrace())|Skips->RSkips|Todos->RTodos|s->RError(Printexc.to_strings,backtrace())inletposition=matchresultwith|RSuccess|RSkip_|RTodo_|RTimeout_->None|RFailure_|RError_->OUnitLogger.positionctxt.loggerinctxt.path,result,positionletreport_result_fullctxtresult_full=lettest_path,result,_=result_fullinOUnitLogger.reportctxt.logger(OUnitLogger.TestEvent(test_path,OUnitLogger.EResultresult));result_full(** Isolate a function inside a context, just as [!section_ctxt] but don't
propagate a failure, register it for later.
*)letnon_fatalctxtf=trysection_ctxtctxtfwithe->letresult_full=report_result_fullctxt(result_full_of_exceptionctxte)inOUnitShared.Mutex.with_lockctxt.sharedctxt.non_fatal_mutex(fun()->ctxt.non_fatal:=result_full::!(ctxt.non_fatal))(* Some shorthands which allows easy test construction *)let(>:)st=TestLabel(s,t)(* infix *)let(>::)sf=TestLabel(s,TestCase(Short,f))(* infix *)let(>:::)sl=TestLabel(s,TestList(l))(* infix *)(* Utility function to manipulate test *)letrectest_decorateg=function|TestCase(l,f)->TestCase(l,gf)|TestListtst_lst->TestList(List.map(test_decorateg)tst_lst)|TestLabel(str,tst)->TestLabel(str,test_decorategtst)(* Return the number of available tests *)letrectest_case_count=function|TestCase_->1|TestLabel(_,t)->test_case_countt|TestListl->List.fold_left(funct->c+test_case_countt)0lletstring_of_node=function|ListItemn->string_of_intn|Labels->smodulePath=structtypet=pathletcomparep1p2=Stdlib.comparep1p2letto_stringp=String.concat":"(List.rev_mapstring_of_nodep)endmoduleMapPath=Map.Make(Path)letstring_of_path=Path.to_string(* Returns all possible paths in the test. The order is from test case
to root.
*)lettest_case_pathstest=letrectcpspathtest=matchtestwith|TestCase_->[path]|TestListtests->List.concat(mapi(funti->tcps((ListItemi)::path)t)tests)|TestLabel(l,t)->tcps((Labell)::path)tintcps[]test(* Test filtering with their path *)moduleSetTestPath=Set.Make(String)lettest_filter?(skip=false)onlytest=letset_test=List.fold_left(funststr->SetTestPath.addstrst)SetTestPath.emptyonlyinletrecfilter_testpathtst=ifSetTestPath.mem(string_of_pathpath)set_testthenbeginSometstendelsebeginmatchtstwith|TestCase(l,_)->beginifskipthenSome(TestCase(l,fun_->raise(Skip"Test disabled")))elseNoneend|TestListtst_lst->beginletntst_lst=fold_lefti(funntst_lsttsti->letnntst_lst=matchfilter_test((ListItemi)::path)tstwith|Sometst->tst::ntst_lst|None->ntst_lstinnntst_lst)[]tst_lstinifnotskip&&ntst_lst=[]thenNoneelseSome(TestList(List.revntst_lst))end|TestLabel(lbl,tst)->beginletntst_opt=filter_test((Labellbl)::path)tstinmatchntst_optwith|Somentst->Some(TestLabel(lbl,ntst))|None->ifskipthenSome(TestLabel(lbl,tst))elseNoneendendinfilter_test[]test