package ppx_windtrap
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
PPX and instrumentation backend for windtrap
Install
dune-project
Dependency
Authors
Maintainers
Sources
windtrap-0.2.0.tbz
sha256=2e61a86f8c97502c1f8a593c28e255d591a44b1533c6eed8cd0c847afa3d772a
sha512=060d0a47d926d420e0f96f4912c2690d148d6196bcbb701b2463a4fb9ffade5c4ff4d98d703f4b80b2b8ad1bed848c68f1d613e3ba745f9cd5e7d313e049bdc3
doc/src/ppx_windtrap.runtime/ppx_runtime.ml.html
Source file ppx_runtime.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(*--------------------------------------------------------------------------- Copyright (c) 2026 Invariant Systems. All rights reserved. SPDX-License-Identifier: ISC ---------------------------------------------------------------------------*) (* Not mutated: this module judges mutants (see ppx/runtime/dune). *) [@@@mutate exclude_file] (* The registry *) (* A registration's library, [None] for code of no library, and its partition, the basename of its file. *) type origin = string option * string (* An open [module%test] group. *) type frame = { name : string; tags : string list; origin : origin; names : (string, int ref) Hashtbl.t; (* how many children took each name *) mutable children : Windtrap.test list; (* newest first *) } (* Module initializers fill the registry before [init] reads the protocol; [collect] drains the top level. *) let groups : frame list ref = ref [] (* the open groups, innermost first *) let top_level : (origin * Windtrap.test) list ref = ref [] (* newest first *) (* How many top-level registrations took each name, per library and module. *) let top_names : (string option * string * string, int ref) Hashtbl.t = Hashtbl.create 16 (* Every origin that registered, drained or not, newest first. *) let origins : origin list ref = ref [] (* The undriven guard *) let claimed = ref false let partitions_of drives = List.sort_uniq String.compare (List.filter_map (fun (library, partition) -> if drives library then Some partition else None) !origins) (* [Windtrap.run] installs its exit guard mid-run, after every registration, so this handler runs after it. Each [at_exit] handler runs at most once, so the [Stdlib.exit] here still runs the others, a coverage dump included. A forked child inherits the handler and must stay silent. *) let undriven_guard = lazy (let owner = Unix.getpid () in at_exit (fun () -> if (not !claimed) && Unix.getpid () = owner then begin Printf.eprintf "windtrap: registered inline tests were never driven: this \ executable links ppx_windtrap-preprocessed test code of no \ library (%s) and nothing ran it.\n\ windtrap: move the tests into a library stanza with \ (inline_tests), whose inline runner dune builds and drives, or \ drive the runner protocol yourself \ (Ppx_windtrap_runtime.Ppx_runtime.init/exit). Exiting 2: nothing \ ran.\n\ %!" (String.concat ", " (partitions_of Option.is_none)); Stdlib.exit 2 end)) (* Registration *) let module_name partition = String.capitalize_ascii (List.hd (String.split_on_char '.' partition)) (* The runner refuses two tests of one path. *) let rec unique names key name = match Hashtbl.find_opt names (key name) with | None -> Hashtbl.add names (key name) (ref 1); name | Some n -> incr n; unique names key (Printf.sprintf "%s (%d)" name !n) let note_origin ?library file = if Option.is_none library then Lazy.force undriven_guard; let origin = (library, Filename.basename file) in origins := origin :: !origins; origin let scoped_name (library, partition) name = match !groups with | [] -> unique top_names (fun name -> (library, module_name partition, name)) name | frame :: _ -> unique frame.names Fun.id name let register origin tree = match !groups with | [] -> top_level := (origin, tree) :: !top_level | frame :: _ -> frame.children <- tree :: frame.children let add_test ?library ~file ~pos ~ name fn = let origin = note_origin ?library file in let name = scoped_name origin name in register origin (Windtrap.test ~__POS__:pos ~tags name fn) let enter_group ?library ~file ~ name = let origin = note_origin ?library file in let name = scoped_name origin name in groups := { name; tags; origin; names = Hashtbl.create 8; children = [] } :: !groups let leave_group () = match !groups with | [] -> invalid_arg "Ppx_runtime.leave_group: no group is open" | frame :: rest -> groups := rest; register frame.origin (Windtrap.group ~tags:frame.tags frame.name (List.rev frame.children)) (* Expect tests *) module Baseline = Windtrap.Private.Baseline module Loc = Windtrap.Private.Loc module Run = Windtrap.Private.Run (* The nodes that the running expect test reached. Tests do not nest, so one table serves them all. *) let reached : (Windtrap.pos, unit) Hashtbl.t = Hashtbl.create 16 let reach node = Hashtbl.replace reached node () let unreached_message others = let line (_, line, _, _) = string_of_int line in let head = "the body returned without reaching this node" in match others with | [] -> head | [ other ] -> Printf.sprintf "%s, nor the node of line %s" head (line other) | others -> Printf.sprintf "%s, nor the nodes of lines %s" head (String.concat ", " (List.map line others)) (* The delimiter keeps a failure raised in the body's tail position from being located in this function. The table is reset first: an attempt that raised leaves its marks behind. *) let expect_test ~pos ~body_end ~nodes body output = Hashtbl.reset reached; Loc.delimit body; Run.check_baseline ~loc:(Loc.of_pos body_end) (Baseline.Trailing { pos }) (output ()); match List.filter (fun node -> not (Hashtbl.mem reached node)) nodes with | [] -> () | first :: others -> Windtrap.fail ~__POS__:first (unreached_message others) (* The runner protocol *) (* [runner] is the library of [inline-test-runner <lib>], and [None] outside the runner mode. *) type protocol = { prog : string; runner : string option; partition : string option; list_partitions : bool; } let unread = { prog = ""; runner = None; partition = None; list_partitions = false } let protocol = ref unread let init argv = claimed := true; let rec parse p = function | "inline-test-runner" :: library :: rest -> parse { p with runner = Some library } rest | "-partition" :: file :: rest -> parse { p with partition = Some file } rest | "-list-partitions" :: rest -> parse { p with list_partitions = true } rest | _ :: rest -> parse p rest | [] -> p in let prog = if Array.length argv > 0 then argv.(0) else "" in protocol := parse { unread with prog } (Array.to_list argv) let drives library = Option.is_none library || Option.equal String.equal library !protocol.runner (* [init] runs after the test modules have loaded, so the partition is applied here and not at registration. *) let collect () = claimed := true; match !groups with | _ :: _ -> invalid_arg "Ppx_runtime.collect: a module%test group was never closed" | [] -> let registered = List.rev !top_level in top_level := []; Hashtbl.reset top_names; let in_partition partition = match !protocol.partition with | None -> true | Some wanted -> String.equal partition wanted in let by_module = Hashtbl.create 16 and first_seen = ref [] in List.iter (fun ((library, partition), tree) -> if drives library && in_partition partition then let name = module_name partition in match Hashtbl.find_opt by_module name with | Some trees -> trees := tree :: !trees | None -> first_seen := name :: !first_seen; Hashtbl.add by_module name (ref [ tree ])) registered; List.rev_map (fun name -> Windtrap.group name (List.rev !(Hashtbl.find by_module name))) !first_seen let exit () = let p = !protocol in match (p.runner, p.list_partitions) with | None, _ -> Stdlib.exit 0 | Some _, true -> List.iter print_endline (partitions_of drives); Stdlib.exit 0 | Some library, false -> (* Dune runs a library's partitions concurrently, and a suite's name keys its capture logs, its JUnit file and its last failed tests. *) let suite = match p.partition with | Some file -> library ^ "/" ^ file | None -> library in (* Under [--corrected] a recorded correction leaves the exit code alone: the [diff?] dune runs next is the verdict. *) Stdlib.exit (Windtrap.run ~argv:[| p.prog; "--corrected" |] suite (collect ()))
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>