@@ -595,6 +595,80 @@ let () =
595595 assert_equal ! accumulator 4 ;
596596 ()
597597
598+ let with_temp_path name f =
599+ let path = Filename. concat (Filename. get_temp_dir_name () ) name in
600+ (try Sys. remove path with _ -> () );
601+ Fun. protect ~finally: (fun () -> try Sys. remove path with _ -> () ) (fun () -> f path)
602+
603+ let () = test " Files.save_as writes to new regular file" @@ fun () ->
604+ with_temp_path " test_save_as_new.txt" @@ fun path ->
605+ Files. save_as path (fun oc -> output_string oc " hello\n " );
606+ let content = In_channel. with_open_text path In_channel. input_all in
607+ assert_equal ~printer: id " hello\n " content
608+
609+ let () = test " Files.save_as overwrites existing regular file" @@ fun () ->
610+ with_temp_path " test_save_as_overwrite.txt" @@ fun path ->
611+ Out_channel. with_open_text path (fun oc -> output_string oc " old\n " );
612+ Files. save_as path (fun oc -> output_string oc " new\n " );
613+ let content = In_channel. with_open_text path In_channel. input_all in
614+ assert_equal ~printer: id " new\n " content
615+
616+ let () = test " Files.save_as no temp file left on success" @@ fun () ->
617+ with_temp_path " test_save_as_no_temp.txt" @@ fun path ->
618+ Files. save_as path (fun oc -> output_string oc " data\n " );
619+ let temp = Printf. sprintf " %s.save.%d.tmp" path (U. gettid () ) in
620+ assert_bool " temp file should not exist" (not (Sys. file_exists temp))
621+
622+ let () = test " Files.save_as no temp file left on failure" @@ fun () ->
623+ with_temp_path " test_save_as_fail.txt" @@ fun path ->
624+ (try Files. save_as path (fun _oc -> failwith " boom" ) with Failure _ -> () );
625+ let temp = Printf. sprintf " %s.save.%d.tmp" path (U. gettid () ) in
626+ assert_bool " temp file should not exist" (not (Sys. file_exists temp));
627+ assert_bool " target file should not exist" (not (Sys. file_exists path))
628+
629+ let () = test " Files.save_as writes to /dev/null without error" @@ fun () ->
630+ Files. save_as " /dev/null" (fun oc -> output_string oc " discarded\n " );
631+ let temp = Printf. sprintf " /dev/null.save.%d.tmp" (U. gettid () ) in
632+ assert_bool " temp file should not exist" (not (Sys. file_exists temp));
633+ assert_bool " /dev/null should be a char device" ((Unix. stat " /dev/null" ).st_kind = Unix. S_CHR )
634+
635+ let () = test " Files.save_as writes to named pipe (FIFO)" @@ fun () ->
636+ with_temp_path " test_save_as_fifo" @@ fun fifo_path ->
637+ Unix. mkfifo fifo_path 0o644 ;
638+ let received = ref " " in
639+ let reader = Thread. create (fun () -> received := In_channel. with_open_text fifo_path In_channel. input_all) () in
640+ Files. save_as fifo_path (fun oc -> output_string oc " fifo data\n " );
641+ Thread. join reader;
642+ assert_equal ~printer: id " fifo data\n " ! received
643+
644+ let () = test " Files.save_as no temp file created for FIFO" @@ fun () ->
645+ with_temp_path " test_save_as_fifo2" @@ fun fifo_path ->
646+ Unix. mkfifo fifo_path 0o644 ;
647+ let reader = Thread. create (fun () -> ignore (In_channel. with_open_text fifo_path In_channel. input_all)) () in
648+ Files. save_as fifo_path (fun oc -> output_string oc " data\n " );
649+ Thread. join reader;
650+ let temp = Printf. sprintf " %s.save.%d.tmp" fifo_path (U. gettid () ) in
651+ assert_bool " temp file should not exist" (not (Sys. file_exists temp))
652+
653+ let () = test " Files.save_as writes through symlink without clobbering it" @@ fun () ->
654+ with_temp_path " test_save_as_symlink_target.txt" @@ fun target ->
655+ with_temp_path " test_save_as_symlink_link.txt" @@ fun link ->
656+ Out_channel. with_open_text target (fun oc -> output_string oc " old\n " );
657+ Unix. symlink target link;
658+ Files. save_as link (fun oc -> output_string oc " new\n " );
659+ assert_bool " symlink should still be a symlink" ((Unix. lstat link).st_kind = Unix. S_LNK );
660+ let content = In_channel. with_open_text target In_channel. input_all in
661+ assert_equal ~printer: id " new\n " content
662+
663+ let () = test " Files.save_as fails on broken symlink" @@ fun () ->
664+ with_temp_path " test_save_as_symlink_target.txt" @@ fun target ->
665+ with_temp_path " test_save_as_symlink_link.txt" @@ fun link ->
666+ Unix. symlink target link;
667+ try
668+ Files. save_as link (fun oc -> output_string oc " new\n " );
669+ assert_failure " should fail on broken symlink"
670+ with Unix. Unix_error (Unix. ENOENT, "realpath" , _ ) -> ()
671+
598672let () = test " Logfmt" begin fun () ->
599673 let eq name expected got =
600674 assert_equal ~msg: name ~printer: (fun s -> sprintf " %S" s) expected got
0 commit comments