package b0
Software construction and deployment kit
Install
dune-project
Dependency
Authors
Maintainers
Sources
b0-0.0.6.tbz
sha512=e9aa779e66c08fc763019f16d4706f465d16c05d6400b58fbd0313317ef33ddea51952e2b058db28e65f7ddb7012f328c8bf02d8f1da17bb543348541a2587f0
doc/src/b0.kit/b0_expect.ml.html
Source file b0_expect.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
(*--------------------------------------------------------------------------- Copyright (c) 2022 The b0 programmers. All rights reserved. SPDX-License-Identifier: ISC ---------------------------------------------------------------------------*) open B0_std open Result.Syntax (* FIXME potentially add these things to Fmt/fpath *) let pp_cli_arg fmt = Fmt.st' [`Underline] fmt let fpath_pp_high_suffix pre ppf p = match Fpath.strip_prefix pre p with | None -> (Fmt.code' Fpath.pp) ppf p | Some p -> Fpath.pp ppf pre; (if not (Fpath.is_syntactic_dir pre) then Fmt.char ppf Fpath.natural_dir_sep_char); (Fmt.code' Fpath.pp) ppf p (* Aborting *) exception Abort of string let abort msg = raise (Abort msg) let abortf fmt = Format.kasprintf abort fmt let result_to_abort = function Ok v -> v | Error msg -> abort msg let abort_to_result f = try Ok (f ()) with Abort e -> Error e (* Outcomes *) module Outcome = struct type status = [ `Corrected | `Expected | `New | `Unexpected | `Unknown ] type test = File of { file : Fpath.t; diff : bool } type t = { status : status; test : test } let v status test = { status; test } let status o = o.status let test o = o.test end (* Contexts *) type t = { base : Fpath.t; env : B0_env.t; log_absolute : bool; log_diffs : bool; mutable outcomes : Outcome.t list; mutable seen : Fpath.Set.t; time : Os.Mtime.counter; vcs_repo : B0_vcs_repo.t; } let make ?vcs_repo ?(log_absolute = false) ?(log_diffs = true) env ~base = let scope_dir = B0_env.scope_dir env in let base = Fpath.(scope_dir // base) in let outcomes = [] and seen = Fpath.Set.empty in let time = Os.Mtime.counter () in let vcs_repo = match vcs_repo with | None -> B0_vcs_repo.get ~dir:scope_dir () |> result_to_abort | Some vcs -> vcs in { base; env; log_absolute; log_diffs; outcomes; seen; time; vcs_repo } let base ctx = ctx.base let base_files ?rel ctx ~recurse = result_to_abort @@ Os.Dir.fold_files ?rel ~recurse Os.Dir.path_list ctx.base [] let dur ctx = Os.Mtime.count ctx.time let env ctx = ctx.env let get_unit_exe_file_cmd ctx u = B0_env.unit_exe_file_cmd ctx.env u |> result_to_abort let log_absolute ctx = ctx.log_absolute let log_diffs ctx = ctx.log_diffs let vcs_repo ctx = ctx.vcs_repo let outcomes ctx = ctx.outcomes let cwd_rel_path ctx p = Fpath.relative ~to_dir:(B0_env.cwd ctx.env) p let path_for_user ctx p = if ctx.log_absolute then p else cwd_rel_path ctx p let pp_path_for_user ctx ppf p = match ctx.log_absolute with | true -> fpath_pp_high_suffix (B0_env.scope_dir ctx.env) ppf p | false -> (Fmt.code' Fpath.pp) ppf (cwd_rel_path ctx p) (* Showing results *) let log_diff ctx file = match B0_vcs_repo.kind ctx.vcs_repo with | Git -> let git = B0_vcs_repo.repo_cmd ctx.vcs_repo in let color = match Fmt.styler () with Fmt.Ansi -> true | _ -> false in let color = Cmd.(if' color (arg "--color=always")) in let cmd = Cmd.(git % "--no-pager" % "diff" %% color %% path file) in Log.if_error ~use:() (Os.Cmd.run cmd) | Hg -> failwith "Hg support is TODO" let log_outcome ctx o = let label ppf st l = Fmt.st st ppf (String.concat " " ["";l;""]) in let pp_label ppf = function | `Unexpected -> label ppf [`Bg `Red; `Fg `White] "M" | `New -> label ppf [`Bg `Yellow; `Fg `Black] "?" in match Outcome.test o with | File { file; diff } -> match Outcome.status o with | `Unexpected | `New as st -> Log.stdout (fun m -> m "%a %a" pp_label st (pp_path_for_user ctx) file); if ctx.log_diffs && diff then log_diff ctx file | _ -> () (* Log summary *) let pp_vcs_cmd vcs ?(file = false) ppf cmd = let pp_file_arg ppf () = Fmt.(pp_cli_arg string) ppf "file" in let file = if file then pp_file_arg else Fmt.nop in Fmt.pf ppf "%a %a" Fmt.code (String.concat " " [vcs; cmd]) file () let pp_git = pp_vcs_cmd "git" let pp_hg = pp_vcs_cmd "hg" let pp_new_cmd ppf vcs = match B0_vcs_repo.kind vcs with | Git -> pp_git ~file:true ppf "add" | Hg -> pp_hg ppf ~file:true "TODO" let pp_correct_cmd ppf vcs = match B0_vcs_repo.kind vcs with | Git -> pp_git ~file:true ppf "add -p" | Hg -> pp_hg ~file:true ppf "TODO" let pp_unexpected_cmd ppf vcs = match B0_vcs_repo.kind vcs with | Git -> pp_git ~file:true ppf "diff" | Hg -> pp_hg ppf ~file:true "TODO" let pp_status_cmd ppf (vcs, dir) = match B0_vcs_repo.kind vcs with | Git -> pp_git ppf ("status -s " ^ Fpath.to_string dir) | Hg -> pp_hg ppf "TODO" let pp_diff_cmd ppf (vcs, dir) = match B0_vcs_repo.kind vcs with | Git -> pp_git ppf ("diff " ^ Fpath.to_string dir) | Hg -> pp_hg ppf "TODO" let pp_status st status = Fmt.st' st (fun ppf c -> Fmt.pf ppf "%d %s" c status) let pp_corrected ppf n = if n = 0 then () else Fmt.pf ppf " (%d corrected)" n let pp_expected = pp_status [`Fg `Green] "expected" let pp_unexpected = pp_status [`Fg `Red] "unexpected" let pp_new = pp_status [`Fg `Yellow] "new" let pp_unknown = pp_status [`Fg `Red] "unknown" let pp_expected ppf = function | (0, _) -> () | (n, c) -> Fmt.pf ppf "@,%a%a" pp_expected n pp_corrected c let pp_unexpected ppf = function | (0, _) -> () | (n, vcs) -> Fmt.pf ppf "@,%a (check with %a, correct with %a)" pp_unexpected n pp_unexpected_cmd vcs pp_correct_cmd vcs let pp_new ppf = function | (0, _) -> () | (n, vcs) -> Fmt.pf ppf "@,%a (integrate with %a)" pp_new n pp_new_cmd vcs let pp_unknown ppf = function 0 -> () | n -> Fmt.pf ppf "@,%a" pp_unknown n let pp_all_pass ppf (count, corr, dur) = let test = if count > 1 then "tests expected" else "test expected" in let green = [`Fg `Green] in Fmt.pf ppf "%a %a%a in %a" (Fmt.st green) "All" (pp_status green test) count pp_corrected corr Mtime.Span.pp dur let pp_total ppf (count, dur) = let test = if count > 1 then "tests" else "test" in Fmt.pf ppf "@,%a in %a" (pp_status [`Bold] test) count Mtime.Span.pp dur let log_summary ctx = let expected = ref 0 and unexpected = ref 0 and new' = ref 0 and corrected = ref 0 and unknown = ref 0 in let incr o = match Outcome.status o with | `Expected -> incr expected | `Unexpected -> incr unexpected | `New -> incr new' | `Corrected -> incr expected; incr corrected | `Unknown -> incr unknown in let count = List.length ctx.outcomes in let () = List.iter incr ctx.outcomes in match !expected = count with | true -> Log.stdout (fun m -> m "%a" pp_all_pass (count, !corrected, dur ctx)); Os.Exit.ok | false -> Log.stdout (fun m -> m "@[<v> @[<v>%a%a%a%a%a@]@,@,\ Summary with %a@,Details with %a@]" pp_expected (!expected, !corrected) pp_new (!new', ctx.vcs_repo) pp_unknown !unknown pp_unexpected (!unexpected, ctx.vcs_repo) pp_total (count, dur ctx) pp_status_cmd (ctx.vcs_repo, path_for_user ctx ctx.base) pp_diff_cmd (ctx.vcs_repo, path_for_user ctx ctx.base)); Os.Exit.code 1 (* Primitives *) let outcome_of_file ?(diff = true) ctx file = let file = Fpath.(ctx.base // file) in match B0_vcs_repo.kind ctx.vcs_repo with | Git -> let git = B0_vcs_repo.repo_cmd ctx.vcs_repo in let cmd = Cmd.(git % "status" % "--porcelain" %% path file) in let status = Os.Cmd.run_out ~trim:false cmd |> result_to_abort in let status = match String.take_first 2 status with | "" -> `Expected | "M " | "A " -> `Corrected | "??" -> `New | s when s.[1] = 'M' -> `Unexpected | _ -> `Unknown in Outcome.v status (File { file; diff }) | Hg -> failwith "Hg support is TODO" let check_add ctx o = match Outcome.test o with | File { file; _ } -> if not (Fpath.Set.mem file ctx.seen) then ctx.seen <- Fpath.Set.add file ctx.seen else Fmt.invalid_arg "%a: file already checked. \ Are you trying to write it multiple times ?" Fpath.pp_unquoted file let add_outcome ctx o = check_add ctx o; ctx.outcomes <- o :: ctx.outcomes; log_outcome ctx o let finish ctx = log_summary ctx (* Expectations *) let file ?diff ctx file = add_outcome ctx (outcome_of_file ctx file) let stdout ?diff ctx ?env ?cwd ?stdout cmd = let out = match stdout with | Some stdout -> stdout | None -> Fpath.v (Fpath.basename (Cmd.find_tool cmd |> Option.get) ^ ".stdout") in let out = Fpath.(ctx.base // out) in let stdout = Os.Cmd.out_file ~force:true ~make_path:true out in let () = Os.Cmd.run ?env ?cwd ~stdout cmd |> result_to_abort in file ?diff ctx out let stderr ?diff ctx ?env ?cwd ?stderr cmd = let out = match stderr with | Some stderr -> stderr | None -> Fpath.v (Fpath.basename (Cmd.find_tool cmd |> Option.get) ^ ".stderr") in let out = Fpath.(ctx.base // out) in let stderr = Os.Cmd.out_file ~force:true ~make_path:true out in let _status = Os.Cmd.run_status ?env ?cwd ~stderr cmd |> result_to_abort in file ?diff ctx out (* Cmdlet *) let short = let doc = "Short output, do not output diffs on unexpected or new tests." in Cmdliner.Arg.(value & flag & info ["s"; "short"] ~doc) let log_absolute_arg = let doc = "Output absolute paths instead of having them relative to the \ current working directory." in Cmdliner.Arg.(value & flag & info ["a"; "absolute-paths"] ~doc) let exits = Cmdliner.Cmd.Exit.info 1 ~doc:"on unexpected expectations." :: Cmdliner.Cmd.Exit.defaults let run f env base short log_absolute = Os.Exit.of_result' @@ abort_to_result @@ fun () -> let ctx = make env ~log_absolute ~log_diffs:(not short) ~base in f ctx; finish ctx let action_func ~base f = B0_unit.Action.of_cmdliner_term ~exits @@ fun env u -> let run = run f env base in Cmdliner.Term.(const run $ short $ log_absolute_arg)
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>