@@ -166,6 +166,8 @@ let metadata_entry_repr =
166166type metadata_result =
167167 | Present of metadata_entry
168168 | Missing_unreachable
169+ | Timed_out
170+ | Not_ran
169171
170172let metadata_result_repr =
171173 Repr. variant
@@ -176,6 +178,12 @@ let metadata_result_repr =
176178 ; Repr. case0 " Missing_unreachable" ~test: (function
177179 | Missing_unreachable -> true
178180 | _ -> false )
181+ ; Repr. case0 " Timed_out" ~test: (function
182+ | Timed_out -> true
183+ | _ -> false )
184+ ; Repr. case0 " Not_ran" ~test: (function
185+ | Not_ran -> true
186+ | _ -> false )
179187 ]
180188;;
181189
@@ -275,6 +283,29 @@ let read_and_attach_exit_codes (sh_script : sh_script)
275283 loop [] metadata_entries sh_script.cram_to_output times
276284;;
277285
286+ let mark_timed_out_command =
287+ let rec loop acc = function
288+ | [] -> None
289+ | (Cram_lexer. Comment _ as comment ) :: blocks -> loop (comment :: acc) blocks
290+ | Command ({ metadata = Present _ ; _ } as command ) :: blocks ->
291+ loop (Command command :: acc) blocks
292+ | Command { metadata = Timed_out | Not_ran ; _ } :: _ ->
293+ Code_error. raise " unexpected timeout metadata" []
294+ | Command ({ metadata = Missing_unreachable ; block; _ } as command ) :: blocks ->
295+ (match Fpath. exists (Path. to_string block.output_file) with
296+ | false -> None
297+ | true ->
298+ let blocks =
299+ List. map blocks ~f: (function
300+ | Cram_lexer. Comment _ as comment -> comment
301+ | Command command ->
302+ Command { command with metadata = Not_ran ; duration = None })
303+ in
304+ Some (List. rev acc @ (Command { command with metadata = Timed_out } :: blocks)))
305+ in
306+ fun cram_to_output -> loop [] cram_to_output
307+ ;;
308+
278309let line_number =
279310 let open Re in
280311 seq [ set " 123456789" ; rep digit ]
@@ -344,12 +375,15 @@ let sanitize ~parent_script cram_to_output : command_out Cram_lexer.block list =
344375 | Command { block; metadata; duration = _ } ->
345376 let output =
346377 let output =
347- match Io. read_file ~binary: false block.output_file with
348- | exception _ -> None
349- | contents -> Some (Ansi_color. strip contents)
378+ match metadata with
379+ | Not_ran -> None
380+ | Present _ | Missing_unreachable | Timed_out ->
381+ (match Io. read_file ~binary: false block.output_file with
382+ | exception _ -> None
383+ | contents -> Some (Ansi_color. strip contents))
350384 in
351385 match metadata with
352- | Missing_unreachable -> output
386+ | Missing_unreachable | Not_ran | Timed_out -> output
353387 | Present { build_path_prefix_map; exit_code = _ } ->
354388 Option. map
355389 output
@@ -381,17 +415,26 @@ let compose_cram_output (cram_to_output : _ Cram_lexer.block list) =
381415 let line = sprintf " %c %s" (if i = 0 then '$' else '>' ) line in
382416 add_line_prefixed_with_two_space line);
383417 let unreachable = " ***** UNREACHABLE *****" in
384- let () =
385- Option. value output ~default: unreachable
386- |> String. split_lines
387- |> List. iter ~f: add_line_prefixed_with_two_space
418+ let add_output output =
419+ output |> String. split_lines |> List. iter ~f: add_line_prefixed_with_two_space
420+ in
421+ let add_output_or_unreachable = function
422+ | None -> add_line_prefixed_with_two_space unreachable
423+ | Some output -> add_output output
388424 in
389425 (match metadata with
390426 | Missing_unreachable ->
427+ add_output_or_unreachable output;
391428 Option. iter output ~f: (fun (_ : string ) ->
392429 add_line_prefixed_with_two_space unreachable)
393- | Present { exit_code = 0 ; build_path_prefix_map = _ } -> ()
430+ | Timed_out ->
431+ Option. iter output ~f: add_output;
432+ add_line_prefixed_with_two_space " [timed out]"
433+ | Not_ran -> add_line_prefixed_with_two_space " [not ran]"
434+ | Present { exit_code = 0 ; build_path_prefix_map = _ } ->
435+ add_output_or_unreachable output
394436 | Present { exit_code; build_path_prefix_map = _ } ->
437+ add_output_or_unreachable output;
395438 add_line_prefixed_with_two_space (sprintf " [%d]" exit_code)));
396439 Buffer. contents buf
397440;;
@@ -549,6 +592,75 @@ let make_temp_dir ~script =
549592 temp_dir
550593;;
551594
595+ let raise_timeout_error ~src ~timeout =
596+ (match timeout with
597+ | None -> [ Pp. text " Cram test timed out" ]
598+ | Some (timeout_loc , timeout ) ->
599+ let timeout_set_message =
600+ [ Pp. textf " A time limit of %.2fs has been set in " (Time.Span. to_secs timeout)
601+ ; Pp. tag User_message.Style. Loc @@ Loc. pp_file_colon_line timeout_loc
602+ ]
603+ |> Pp. concat
604+ |> Pp. hovbox
605+ in
606+ [ Pp. text " Cram test timed out" ; timeout_set_message ])
607+ |> User_error. raise
608+ ~loc: (Loc. in_file (Path. drop_optional_build_context_maybe_sandboxed src))
609+ ;;
610+
611+ let combine_with_current_stanzas =
612+ let rec loop acc current expected =
613+ match current with
614+ | [] -> acc
615+ | Cram_lexer. Comment x :: current ->
616+ loop (Cram_lexer. Comment x :: acc) current expected
617+ | Command _ :: current ->
618+ (match expected with
619+ | [] -> acc
620+ | out :: expected -> loop (Cram_lexer. Command out :: acc) current expected)
621+ in
622+ fun current_stanzas expected -> loop [] current_stanzas expected |> List. rev
623+ ;;
624+
625+ let print_timeout_correction_and_fail ~src ~conflict_markers ~timeout command_outputs =
626+ let open Fiber.O in
627+ let * () =
628+ let source_contents = Io. read_file ~binary: false src in
629+ let expected =
630+ let current_stanzas =
631+ Lexbuf. from_string source_contents ~fname: (Path. to_string src)
632+ |> cram_stanzas ~conflict_markers
633+ |> List. map ~f: snd
634+ in
635+ combine_with_current_stanzas current_stanzas command_outputs |> compose_cram_output
636+ in
637+ let corrected_file = Path. extend_basename src ~suffix: Filename. corrected in
638+ if String. equal source_contents expected
639+ then (
640+ Path. rm_rf corrected_file;
641+ Fiber. return () )
642+ else (
643+ Io. write_file ~binary: false corrected_file expected;
644+ let + diff = Dune_engine.Print_diff. get src corrected_file in
645+ let () =
646+ let source_file = Path. drop_optional_build_context_src_exn src in
647+ let correction_file = Path. as_in_build_dir_exn corrected_file in
648+ Dune_engine.Diff_promotion. register_intermediate
649+ `Move
650+ ~source_file
651+ ~correction_file
652+ in
653+ match diff with
654+ | Error message -> Console. print_user_message message
655+ | Ok diff ->
656+ Dune_engine.Print_diff.Diff. print diff;
657+ (* The timeout error below is printed on stderr, so flush the diff first to
658+ keep the correction before the error in combined output. *)
659+ flush stdout)
660+ in
661+ raise_timeout_error ~src ~timeout
662+ ;;
663+
552664let run_cram_test
553665 env
554666 ~src
@@ -620,42 +732,9 @@ let run_cram_test
620732 |> Dune_trace.Event.Cram. test ~test: src);
621733 sanitize ~parent_script: script detailed_output
622734 | Error `Timed_out ->
623- let timeout_set_message =
624- let timeout_loc, timeout = Option. value_exn timeout in
625- [ Pp. textf " A time limit of %.2fs has been set in " (Time.Span. to_secs timeout)
626- ; Pp. tag User_message.Style. Loc @@ Loc. pp_file_colon_line timeout_loc
627- ]
628- |> Pp. concat
629- |> Pp. hovbox
630- in
631- let timeout_msg =
632- match
633- let completed_count =
634- read_exit_codes_and_prefix_maps sh_script.metadata_file |> List. length
635- in
636- let command_blocks_only =
637- List. filter_map sh_script.cram_to_output ~f: (function
638- | Cram_lexer. Comment _ -> None
639- | Command block_result -> Some block_result)
640- in
641- let total_commands = List. length command_blocks_only in
642- if completed_count < total_commands
643- then (
644- (* Find the command that got stuck - it's the one at index completed_count *)
645- match List. nth command_blocks_only completed_count with
646- | Some { command; _ } -> Some (String. concat ~sep: " " command)
647- | None -> None )
648- else None
649- with
650- | None -> [ Pp. text " Cram test timed out" ]
651- | Some cmd ->
652- [ Pp. textf " Cram test timed out while running command:"
653- ; Pp. verbatimf " $ %s" cmd
654- ]
655- in
656- User_error. raise
657- ~loc: (Loc. in_file (Path. drop_optional_build_context_maybe_sandboxed src))
658- (timeout_msg @ [ timeout_set_message ])
735+ (match read_and_attach_exit_codes sh_script |> mark_timed_out_command with
736+ | None -> raise_timeout_error ~src ~timeout
737+ | Some detailed_output -> sanitize ~parent_script: script detailed_output)
659738;;
660739
661740let run_produce_correction
@@ -691,7 +770,7 @@ module Script = Persistent.Make (struct
691770
692771 let name = " CRAM-RESULT"
693772 let sharing = false
694- let version = 2
773+ let version = 3
695774 let repr = Repr. list command_out_repr
696775 end )
697776
@@ -706,35 +785,44 @@ let run_and_produce_output
706785 ~setup_scripts
707786 shell
708787 =
709- let cram_stanzas =
710- Io. read_file ~binary: false script
711- |> Lexbuf. from_string ~fname: (Path. to_string script)
712- |> cram_stanzas ~conflict_markers
713- |> List. map ~f: snd
714- in
715- let temp_dir = make_temp_dir ~script in
716- (* We don't want the ".cram.run.t" dir around when executing the script. *)
717- Path. rm_rf (Path. parent_exn script);
718- let open Fiber.O in
719- let + commands =
720- let env = make_run_env env ~temp_dir ~cwd in
721- run_cram_test
722- env
723- ~src
724- ~script
725- ~cram_stanzas
726- ~temp_dir
727- ~cwd
728- ~timeout
729- ~setup_scripts
730- shell
731- >> | List. filter_map ~f: (function
788+ let open Fiber.O in
789+ let * commands =
790+ let + cram_to_output =
791+ let cram_stanzas =
792+ Io. read_file ~binary: false script
793+ |> Lexbuf. from_string ~fname: (Path. to_string script)
794+ |> cram_stanzas ~conflict_markers
795+ |> List. map ~f: snd
796+ in
797+ (* We don't want the ".cram.run.t" dir around when executing the script. *)
798+ Path. rm_rf (Path. parent_exn script);
799+ let temp_dir = make_temp_dir ~script in
800+ let env = make_run_env env ~temp_dir ~cwd in
801+ run_cram_test
802+ env
803+ ~src
804+ ~script
805+ ~cram_stanzas
806+ ~temp_dir
807+ ~cwd
808+ ~timeout
809+ ~setup_scripts
810+ shell
811+ in
812+ List. filter_map cram_to_output ~f: (function
732813 | Cram_lexer. Command c -> Some c
733814 | Comment _ -> None )
734815 in
735- let dst = Path. build dst in
736- Path. mkdir_p (Path. parent_exn dst);
737- Script. dump dst commands
816+ if
817+ List. exists commands ~f: (function
818+ | { metadata = Timed_out ; _ } -> true
819+ | _ -> false )
820+ then print_timeout_correction_and_fail ~src ~conflict_markers ~timeout commands
821+ else (
822+ let dst = Path. build dst in
823+ Path. mkdir_p (Path. parent_exn dst);
824+ Script. dump dst commands;
825+ Fiber. return () )
738826;;
739827
740828module Run = struct
@@ -750,7 +838,7 @@ module Run = struct
750838 }
751839
752840 let name = " cram-run"
753- let version = 6
841+ let version = 7
754842 let runs_process = true
755843 let can_run_in_action_runner = true
756844
@@ -867,7 +955,7 @@ module Diff = struct
867955 }
868956
869957 let name = " cram-generate"
870- let version = 1
958+ let version = 2
871959 let runs_process = false
872960 let can_run_in_action_runner = false
873961 let bimap { script; out } f _ = { script = f script; out = f out }
@@ -889,17 +977,7 @@ module Diff = struct
889977 |> cram_stanzas ~conflict_markers: Ignore
890978 |> List. map ~f: snd
891979 in
892- let rec loop acc current expected =
893- match current with
894- | [] -> acc
895- | Cram_lexer. Comment x :: current ->
896- loop (Cram_lexer. Comment x :: acc) current expected
897- | Command _ :: current ->
898- (match expected with
899- | [] -> acc
900- | out :: expected -> loop (Cram_lexer. Command out :: acc) current expected)
901- in
902- loop [] current_stanzas out |> List. rev
980+ combine_with_current_stanzas current_stanzas out
903981 in
904982 let expected = compose_cram_output combined in
905983 let corrected_file = Path. extend_basename script ~suffix: Filename. corrected in
0 commit comments