File: test_build_helpers.ml

package info (click to toggle)
ocaml-obuild 0.2.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,456 kB
  • sloc: ml: 14,491; sh: 211; ansic: 34; makefile: 11
file content (166 lines) | stat: -rw-r--r-- 5,115 bytes parent folder | download
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
open Filepath
open Filesystem

(** Build testing helper functions *)

(** Create a temporary directory for testing *)
let create_temp_dir prefix =
  let temp_base = try Sys.getenv "TMPDIR" with Not_found -> "/tmp" in
  let rec try_create n =
    if n > 100 then
      failwith "Could not create temporary directory after 100 attempts"
    else
      let dir_name = Printf.sprintf "%s/%s_%d_%d" temp_base prefix (Unix.getpid ()) n in
      try
        Unix.mkdir dir_name 0o755;
        dir_name
      with Unix.Unix_error (Unix.EEXIST, _, _) ->
        try_create (n + 1)
  in
  try_create 0

(** Remove directory recursively *)
let rec remove_dir_recursive dir =
  if Sys.file_exists dir then (
    if Sys.is_directory dir then (
      let entries = Sys.readdir dir in
      Array.iter (fun entry ->
        remove_dir_recursive (Filename.concat dir entry)
      ) entries;
      Unix.rmdir dir
    ) else
      Unix.unlink dir
  )

(** Write content to file, creating parent directories if needed *)
let write_file_with_dirs filepath content =
  let dir = Filename.dirname filepath in
  (* Create parent directories *)
  let rec create_parents path =
    if not (Sys.file_exists path) then (
      create_parents (Filename.dirname path);
      if not (Sys.file_exists path) then
        Unix.mkdir path 0o755
    )
  in
  create_parents dir;
  (* Write file *)
  let oc = open_out filepath in
  output_string oc content;
  close_out oc

(** Create temporary project with files and obuild config *)
let with_temp_build_project ~name ~files ~obuild_content ~test_fn =
  let temp_dir = create_temp_dir ("obuild_test_" ^ name) in
  try
    (* Write all project files *)
    List.iter (fun (filename, content) ->
      let filepath = Filename.concat temp_dir filename in
      write_file_with_dirs filepath content
    ) files;

    (* Write .obuild file *)
    let obuild_file = Filename.concat temp_dir (name ^ ".obuild") in
    write_file_with_dirs obuild_file obuild_content;

    (* Run the test function *)
    test_fn temp_dir;

    (* Cleanup *)
    remove_dir_recursive temp_dir
  with e ->
    (* Cleanup on error *)
    remove_dir_recursive temp_dir;
    raise e

(** Run obuild command and capture output *)
let run_obuild_command ~project_dir ~command ~args =
  let obuild_exe = Filename.concat (Unix.getcwd ()) "dist/build/obuild/obuild" in

  (* Build command line *)
  let cmd_args = obuild_exe :: command :: args in
  let cmd_line = String.concat " " (List.map Filename.quote cmd_args) in

  (* Save current directory *)
  let orig_dir = Unix.getcwd () in

  try
    (* Change to project directory *)
    Unix.chdir project_dir;

    (* Execute command and capture output *)
    let ic = Unix.open_process_in (cmd_line ^ " 2>&1") in
    let output = ref [] in
    (try
      while true do
        output := input_line ic :: !output
      done
    with End_of_file -> ());
    let status = Unix.close_process_in ic in

    (* Return to original directory *)
    Unix.chdir orig_dir;

    (* Check exit status *)
    let success = match status with
      | Unix.WEXITED 0 -> true
      | _ -> false
    in

    let output_str = String.concat "\n" (List.rev !output) in
    (success, output_str)

  with e ->
    (* Make sure we return to original directory *)
    Unix.chdir orig_dir;
    raise e

(** Get file modification time, returns None if file doesn't exist *)
let get_mtime filepath =
  try
    let stats = Unix.stat filepath in
    Some stats.Unix.st_mtime
  with Unix.Unix_error (Unix.ENOENT, _, _) ->
    None

(** Touch file to update its modification time *)
let touch_file filepath =
  let now = Unix.time () in
  Unix.utimes filepath now now

(** Sleep for a short duration (for mtime differences) *)
(** Use 2 seconds to ensure filesystem mtime resolution *)
let short_sleep () =
  ignore (Unix.select [] [] [] 1.1)

(** Assert that file exists *)
let assert_file_exists filepath =
  if not (Sys.file_exists filepath) then
    failwith (Printf.sprintf "Expected file to exist: %s" filepath)

(** Assert that file does not exist *)
let assert_file_not_exists filepath =
  if Sys.file_exists filepath then
    failwith (Printf.sprintf "Expected file to not exist: %s" filepath)

(** Assert that mtime1 < mtime2 *)
let assert_mtime_newer ~msg mtime1_opt mtime2_opt =
  match (mtime1_opt, mtime2_opt) with
  | (Some mtime1, Some mtime2) ->
      if not (mtime2 > mtime1) then
        failwith (Printf.sprintf "%s (mtime1=%.2f, mtime2=%.2f)" msg mtime1 mtime2)
  | (None, _) ->
      failwith (Printf.sprintf "%s (first file doesn't exist)" msg)
  | (_, None) ->
      failwith (Printf.sprintf "%s (second file doesn't exist)" msg)

(** Assert that mtime is unchanged *)
let assert_mtime_unchanged ~msg mtime1_opt mtime2_opt =
  match (mtime1_opt, mtime2_opt) with
  | (Some mtime1, Some mtime2) ->
      if mtime1 <> mtime2 then
        failwith (Printf.sprintf "%s (mtime changed from %.2f to %.2f)" msg mtime1 mtime2)
  | (None, _) ->
      failwith (Printf.sprintf "%s (first file doesn't exist)" msg)
  | (_, None) ->
      failwith (Printf.sprintf "%s (second file doesn't exist)" msg)