Skip to content

Commit

Permalink
Initial implementation of nestable test API
Browse files Browse the repository at this point in the history
  • Loading branch information
craigfe committed May 10, 2021
1 parent b8222f6 commit 521e29e
Show file tree
Hide file tree
Showing 74 changed files with 1,859 additions and 995 deletions.
13 changes: 13 additions & 0 deletions src/alcotest-async/alcotest_async.ml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,16 @@ module V1 = struct
end

include V1

module Unstable = struct
module Tester =
Alcotest_engine.Unstable.Cli.Make (Alcotest.Unix_platform) (Promise)

include Tester

let test_sync ?here ?tags ~name fn =
test ?here ?tags ~name (fun x -> Deferred.return (fn x))

let test ?(timeout = sec 2.) ?here ?tags ~name fn =
test ?here ?tags ~name (run_test timeout name fn)
end
17 changes: 17 additions & 0 deletions src/alcotest-async/alcotest_async_intf.ml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,21 @@ module type Alcotest_async = sig
module V1 : V1
(** An alias of the above API that provides a stability guarantees over major
version changes. *)

module Unstable : sig
open Alcotest_engine.Unstable

include
Cli.S
with type 'a m := 'a Async_kernel.Deferred.t
and type 'a test_args := 'a
and type config := Config.User.t
and type tag_set := Tag.Set.t

val test :
?timeout:Core_kernel.Time.Span.t ->
(('a -> unit Async_kernel.Deferred.t) -> 'a test) Core.identified

val test_sync : (('a -> unit) -> 'a test) Core.identified
end
end
3 changes: 2 additions & 1 deletion src/alcotest-async/dune
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
(name alcotest_async)
(public_name alcotest-async)
(libraries
alcotest.engine
alcotest
alcotest.engine
alcotest.stdlib_ext
async_kernel
async_unix
base
Expand Down
11 changes: 11 additions & 0 deletions src/alcotest-engine/alcotest_engine.ml
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
open! Import

module V1 = struct
module Test = Test
module Core = Core.V1
module Cli = Cli.V1
end

module Unstable = struct
module Cli = Cli.Unstable
module Config = Config
module Core = Core.Unstable
module Source_code_position = Source_code_position
module Tag = Tag
module Test = Test
end

module Monad = Monad
module Platform = Platform

Expand Down
13 changes: 13 additions & 0 deletions src/alcotest-engine/alcotest_engine.mli
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
to defined tests. The platform-specific runners for these tests are in
[alcotest], [alcotest-lwt], [alcotest-async] and [alcotest-mirage]. *)

open! Import

module V1 : sig
(** Version 1 of the user-facing Alcotest API. *)

Expand All @@ -43,6 +45,17 @@ module V1 : sig
(** Wraps {!Core} to provide a command-line interface. *)
end

module Unstable : sig
module Test = Test
(** Unstable version of the user-facing Alcotest API. *)

module Core = Core.Unstable
module Cli = Cli.Unstable
module Tag = Tag
module Config = Config
module Source_code_position = Source_code_position
end

module Monad = Monad
(** Monad signatures for use with {!Core} and {!Cli}. *)

Expand Down
234 changes: 140 additions & 94 deletions src/alcotest-engine/cli.ml
Original file line number Diff line number Diff line change
Expand Up @@ -18,113 +18,159 @@
include Cli_intf
open! Import
open Cmdliner
open Cmdliner_syntax

module Make (P : Platform.MAKER) (M : Monad.S) :
V1_types.S with type return = unit M.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}. *)
let set_color (module Platform : Platform.S) =
let env = Arg.env_var "ALCOTEST_COLOR" in
let+ color_flag =
let enum = [ ("auto", `Auto); ("always", `Ansi_tty); ("never", `None) ] in
let color = Arg.enum enum in
let enum_alts = Arg.doc_alts_enum enum in
let doc =
Fmt.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")
in
Arg.(
value & opt (some color) None & info [ "color" ] ~env ~doc ~docv:"WHEN")
in
let style_renderer =
match color_flag with
| Some `Auto -> None
| Some (`Ansi_tty | `None) as a -> a
| None -> (
try
(* Default to [always] when running inside Dune *)
let (_ : string) = Sys.getenv "INSIDE_DUNE" in
Some `Ansi_tty
with Not_found -> None)
in
Platform.setup_std_outputs ?style_renderer ()

module C = Core.V1.Make (P) (M)
include C
module Make (P : Platform.MAKER) (M : Monad.S) = struct
module C = Core.Unstable.Make (P) (M)
module P = P (M)
open Cmdliner_syntax

let set_color =
let env = Arg.env_var "ALCOTEST_COLOR" in
let+ color_flag =
let enum = [ ("auto", `Auto); ("always", `Ansi_tty); ("never", `None) ] in
let color = Arg.enum enum in
let enum_alts = Arg.doc_alts_enum enum in
let doc =
Fmt.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")
include C

let run ?here ?(config = Config.User.create ()) ~name suite =
let test_command =
let term =
let+ () = set_color (module P)
and+ cli_config = Config.User.term ~and_exit:false in
let config = Config.User.(cli_config || config) in
run ~name ?here ~config suite
in
Arg.(
value & opt (some color) None & info [ "color" ] ~env ~doc ~docv:"WHEN")
in
let style_renderer =
match color_flag with
| Some `Auto -> None
| Some (`Ansi_tty | `None) as a -> a
| None -> (
try
(* Default to [always] when running inside Dune *)
let (_ : string) = Sys.getenv "INSIDE_DUNE" in
Some `Ansi_tty
with Not_found -> None)
(term, Term.info "test")
in
P.setup_std_outputs ?style_renderer ()

let default_cmd ~and_exit config args library_name tests =
let exec_name = Filename.basename Sys.argv.(0) in
let doc = "Run all the tests." in
let term =
let+ () = set_color
and+ cli_config = Config.User.term ~and_exit
and+ args = args in
let config = Config.User.(cli_config || config) in
run_with_args' config library_name args tests
in
(term, Term.info exec_name ~doc)

let test_cmd config args library_name tests =
let doc = "Run a subset of the tests." in
let term =
let+ () = set_color
and+ cli_config = Config.User.term ~and_exit:true
and+ args = args in
let config = Config.User.(cli_config || config) in
run_with_args' config library_name args tests
in
(term, Term.info "test" ~doc)

let list_cmd tests =
let doc = "List all available tests." in
( (let+ () = set_color in
list_tests tests),
Term.info "list" ~doc )

let run_with_args' (type a) ~argv config name (args : a Term.t)
(tl : a test list) =
let and_exit = Config.User.and_exit config in
let ( >>= ) = M.bind in
let choices = [ list_cmd tl; test_cmd config args name tl ] in
let exit_or_return result =
if and_exit then exit (Term.exit_status_of_result result) else M.return ()
in
let result =
Term.eval_choice ?argv
~catch:and_exit (* Only log exceptions not raised to the user code *)
(default_cmd ~and_exit config args name tl)
choices
let list_command =
let term =
let+ () = set_color (module P) in
list_tests ~name suite
in
(term, Term.info "list")
in
match result with
| `Ok unit_m -> unit_m >>= fun () -> exit_or_return (`Ok ())
let exit_or_return result = exit (Term.exit_status_of_result result) in
match Term.eval_choice test_command [ test_command; list_command ] with
| `Ok f -> M.bind f (fun () -> exit_or_return (`Ok ()))
| (`Help | `Version | `Error `Exn) as result -> exit_or_return result
| `Error (`Parse | `Term) as result ->
exit (Term.exit_status_of_result result)

let run_with_args ?and_exit ?verbose ?compact ?tail_errors ?quick_only
?show_errors ?json ?filter ?log_dir ?bail ?argv =
Config.User.kcreate (run_with_args' ~argv) ?and_exit ?verbose ?compact
?tail_errors ?quick_only ?show_errors ?json ?filter ?log_dir ?bail

let run =
Config.User.kcreate (fun config ?argv name tl ->
run_with_args' config ~argv name (Term.pure ()) tl)
(* TODO *)
end

module Make_v1 : V1_types.MAKER =
functor
(P : Platform.MAKER)
(M : Monad.S)
->
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}. *)

module C = Core.V1.Make (P) (M)
include C
module P = P (M)
open Cmdliner_syntax

let default_cmd ~and_exit config args library_name tests =
let exec_name = Filename.basename Sys.argv.(0) in
let doc = "Run all the tests." in
let term =
let+ () = set_color (module P)
and+ cli_config = Config.User.term ~and_exit
and+ args = args in
let config = Config.User.(cli_config || config) in
run_with_args' config library_name args tests
in
(term, Term.info exec_name ~doc)

let test_cmd config args library_name tests =
let doc = "Run a subset of the tests." in
let term =
let+ () = set_color (module P)
and+ cli_config = Config.User.term ~and_exit:true
and+ args = args in
let config = Config.User.(cli_config || config) in
run_with_args' config library_name args tests
in
(term, Term.info "test" ~doc)

let list_cmd suite_name tests =
let doc = "List all available tests." in
( (let+ () = set_color (module P) in
list_tests suite_name tests),
Term.info "list" ~doc )

let run_with_args' (type a) ~argv config name (args : a Term.t)
(tl : a test list) =
let and_exit = Config.User.and_exit config in
let ( >>= ) = M.bind in
let choices = [ list_cmd name tl; test_cmd config args name tl ] in
let exit_or_return result =
if and_exit then exit (Term.exit_status_of_result result)
else M.return ()
in
let result =
Term.eval_choice ?argv
~catch:and_exit (* Only log exceptions not raised to the user code *)
(default_cmd ~and_exit config args name tl)
choices
in
match result with
| `Ok unit_m -> unit_m >>= fun () -> exit_or_return (`Ok ())
| (`Help | `Version | `Error `Exn) as result -> exit_or_return result
| `Error (`Parse | `Term) as result ->
exit (Term.exit_status_of_result result)

let run_with_args ?and_exit ?verbose ?compact ?tail_errors ?quick_only
?show_errors ?json ?filter:_ ?log_dir ?bail ?argv =
Config.User.kcreate (run_with_args' ~argv) ?and_exit ?verbose ?compact
?tail_errors ?quick_only ?show_errors ?json ?filter:None ?log_dir ?bail

let run ?and_exit ?verbose ?compact ?tail_errors ?quick_only ?show_errors
?json ?filter:_ ?log_dir ?bail ?argv name tl =
Config.User.kcreate
(fun c -> run_with_args' ~argv c name (Term.pure ()) tl)
?and_exit ?verbose ?compact ?tail_errors ?quick_only ?show_errors ?json
?filter:None ?log_dir ?bail
end

module V1 = struct
include V1_types
module Make = Make_v1
end

module Unstable = struct
include Unstable_types
module Make = Make
end
19 changes: 17 additions & 2 deletions src/alcotest-engine/cli_intf.ml
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,27 @@ module V1_types = struct
S with type return = unit M.t
end

module Unstable_types = struct
module type S = sig
include Core.Unstable.S
(** @inline *)
end

module type MAKER = Core.Unstable.MAKER
end

module type Cli = sig
module V1 : sig
module type S = V1_types.S
module type MAKER = V1_types.MAKER

module Make (P : Platform.MAKER) (M : Monad.S) :
V1_types.S with type return = unit M.t
module Make : MAKER
end

module Unstable : sig
module type S = Unstable_types.S
module type MAKER = Unstable_types.MAKER

module Make : MAKER
end
end
Loading

0 comments on commit 521e29e

Please sign in to comment.