123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158(*
Mixed bag of types used by most modules of the library, including private
modules.
Some of these types are exported by the main module Testo.
Feel free to isolate types in their own modules if it makes things clearer.
*)(* This indicates whether the test function succeeded without checking
the captured output. *)typecompletion_status=|Test_function_returned|Test_function_raised_an_exception|Test_timeout(* If a test failed with an exception, we don't check the output.
Incorrect_output implies the test returned without raising an exception. *)typefail_reason=Raised_exception|Incorrect_output|Timeout(*
The outcome defines whether a test succeeded or failed.
A test succeeded if the test function returned without raising an
exception and all the captured output, if any, matches the expected output.
This should not be confused with the passing status indicating whether
the test passes: a test passes if its outcome matches the expected outcome.
*)typeoutcome=Succeeded|Failedoffail_reasontypemissing_files=Missing_filesofFpath.tlist(* A summary of the 'status' object using the same language as pytest.
PASS: expected success, actual success
FAIL: expected success, actual failure
XFAIL: expected failure, actual failure
XPASS: expected failure, actual success
MISS: missing data
Maximum string length for display: 5 characters
*)typepassing_status=|PASS|FAILoffail_reason|XFAILoffail_reason|XPASS|MISSofmissing_filestypecaptured_output=|Ignoredofstring(* unchecked combined output *)|Captured_stdoutofstring*string(* stdout, unchecked output *)|Captured_stderrofstring*string(* stderr, unchecked output *)|Captured_stdout_stderrofstring*string(* stdout, stderr *)|Captured_mergedofstring(* combined output *)typeexpected_output=|Ignored|Expected_stdoutofstring|Expected_stderrofstring|Expected_stdout_stderrofstring*string(* stdout, stderr *)|Expected_mergedofstring(* combined output *)typeresult={completion_status:completion_status;captured_output:captured_output;}typeexpected_outcome=|Should_succeed|Should_failofstring(* explains why we expect this test to fail *)(*
The expected output is optional so as to allow new tests for which
there's no expected output yet but there's an expected outcome defined
in the test suite.
*)typeexpectation={expected_outcome:expected_outcome;expected_output:(expected_output,missing_files)Result.t;}(*
Usually, a successful status is one where both result and expectation
are not None and are identical. It can be useful to distinguish various
other statuses such as: missing expectation, missing result, xpass
(success when expected outcome was Failed), ...
*)typestatus={expectation:expectation;result:(result,missing_files)Result.t;}(* Redundant but convenient to consult *)typestatus_summary={passing_status:passing_status;outcome:outcome;has_expected_output:bool;}typechecked_output_options={(* If specified, this is where the output file will be stored instead
of the default location. A relative path is recommended. *)expected_output_path:Fpath.toption;}typechecked_output_kind=|Ignore_output|Stdoutofchecked_output_options|Stderrofchecked_output_options|Stdxxxofchecked_output_options|Split_stdout_stderrofchecked_output_options*checked_output_options(* public *)typetest={(* The ID will be used as a compact key
for referencing tests in filters and in file names.
It's a hash of the internal full name. Both must be unique. *)id:string;(* 'internal_full_name' is derived from 'category' and 'name' and is not
expected to change. It may be used for display purposes but we could
choose to display the test name differently in the future. *)internal_full_name:string;category:stringlist;name:string;func:unit->unitPromise.t;(* Options (alphabetical order) *)broken:stringoption;checked_output:checked_output_kind;expected_outcome:expected_outcome;max_duration(* seconds *):floatoption;normalize:(string->string)list;skipped:stringoption;solo:stringoption;tags:Testo_util.Tag.tlist;tolerate_chdir:bool;tracking_url:stringoption;}typetest_with_status=test*status*status_summary(* TODO: move to a module that has an mli? *)(* "path > to > name" *)letrecompute_internal_full_name(test:test)=String.concat" > "(test.category@[test.name])(*
Compare the captured output that is checked and ignore the unchecked output.
*)letequal_checked_output(a:expected_output)(b:captured_output)=leteq=String.equalinmatch(a,b)with|Ignored,Ignored_->true|Expected_stdoutout,Captured_stdout(out2,_)->eqoutout2|Expected_stderrerr,Captured_stderr(err2,_)->eqerrerr2|Expected_mergeddata,Captured_mergeddata2->eqdatadata2|Expected_stdout_stderr(out,err),Captured_stdout_stderr(out2,err2)->eqoutout2&&eqerrerr2|_->false