| 1 | %%% Copyright (C) Dominic Williams |
|---|
| 2 | %%% All rights reserved. |
|---|
| 3 | %%% See file COPYING. |
|---|
| 4 | |
|---|
| 5 | -module (compiler_test). |
|---|
| 6 | -test (exports). |
|---|
| 7 | -export ([compiles_and_reports_progress_warnings_and_errors/0]). |
|---|
| 8 | -export ([provides_binaries_when_all_compiled/0]). |
|---|
| 9 | -export ([recompiles_minimally_after_change/0]). |
|---|
| 10 | -export ([stops/0]). |
|---|
| 11 | -export ([can_start_empty_and_add_files/0]). |
|---|
| 12 | -export ([notifies_removed_files/0]). |
|---|
| 13 | -export ([notifies_removed_files_even_if_never_compiled/0]). |
|---|
| 14 | -export ([provides_all_binaries_not_just_latest_when_all_compiled/0]). |
|---|
| 15 | -export ([can_be_given_include_directories/0]). |
|---|
| 16 | -export ([discovers_hrl_files_and_includes_their_path/0]). |
|---|
| 17 | %% -export ([recompiles_includers/0]). |
|---|
| 18 | -export ([adds_root_to_include_path/0]). |
|---|
| 19 | -export ([reset_includes_none/0]). |
|---|
| 20 | -export ([reset_includes_found/0]). |
|---|
| 21 | -export ([can_be_given_several_directories/0]). |
|---|
| 22 | -include_lib("stdlib/include/ms_transform.hrl"). |
|---|
| 23 | |
|---|
| 24 | compiles_and_reports_progress_warnings_and_errors () -> |
|---|
| 25 | F = fun compiles_and_reports_progress_warnings_and_errors/2, |
|---|
| 26 | ok = fixtures: use_tree (tester_test:tree (), F). |
|---|
| 27 | |
|---|
| 28 | compiles_and_reports_progress_warnings_and_errors (Root, _) -> |
|---|
| 29 | Compiler = spawn_link (compiler, init, [notify_me (), [Root]]), |
|---|
| 30 | Compiler ! check, |
|---|
| 31 | Ms = receive_all (), |
|---|
| 32 | Compiler ! stop, |
|---|
| 33 | ok = check (Ms, unknown). |
|---|
| 34 | |
|---|
| 35 | stops () -> |
|---|
| 36 | ok = fixtures: use_tree ([], fun stops/2). |
|---|
| 37 | |
|---|
| 38 | stops (Root, _) -> |
|---|
| 39 | Compiler = spawn_link (compiler, init, [notify_me (), [Root]]), |
|---|
| 40 | Compiler ! check, |
|---|
| 41 | receive_all (), |
|---|
| 42 | Compiler ! {self (), stop}, |
|---|
| 43 | ok = receive {Compiler, bye} -> ok after 500 -> timeout end, |
|---|
| 44 | false = is_process_alive (Compiler), |
|---|
| 45 | ok. |
|---|
| 46 | |
|---|
| 47 | provides_binaries_when_all_compiled () -> |
|---|
| 48 | F = fun provides_binaries_when_all_compiled/2, |
|---|
| 49 | ok = fixtures: use_tree (good_erl_tree (), F). |
|---|
| 50 | |
|---|
| 51 | recompiles_minimally_after_change () -> |
|---|
| 52 | F = fun recompiles_minimally_after_change/2, |
|---|
| 53 | ok = fixtures: use_tree (good_erl_tree (), F). |
|---|
| 54 | |
|---|
| 55 | provides_all_binaries_not_just_latest_when_all_compiled () -> |
|---|
| 56 | F = fun provides_all_binaries_not_just_latest_when_all_compiled/2, |
|---|
| 57 | ok = fixtures: use_tree (bad_erl_tree (), F). |
|---|
| 58 | |
|---|
| 59 | can_start_empty_and_add_files () -> |
|---|
| 60 | F = fun can_start_empty_and_add_files/2, |
|---|
| 61 | ok = fixtures: use_tree ([], F). |
|---|
| 62 | |
|---|
| 63 | notifies_removed_files () -> |
|---|
| 64 | F = fun notifies_removed_files/2, |
|---|
| 65 | ok = fixtures: use_tree (good_erl_tree (), F). |
|---|
| 66 | |
|---|
| 67 | notifies_removed_files_even_if_never_compiled () -> |
|---|
| 68 | F = fun notifies_removed_files_even_if_never_compiled/2, |
|---|
| 69 | Tree = [{file, "hello.erl", "rubbish"}], |
|---|
| 70 | ok = fixtures: use_tree (Tree, F). |
|---|
| 71 | |
|---|
| 72 | can_be_given_several_directories () -> |
|---|
| 73 | Tree = [{directory, "src1", good_erl_tree ()}, |
|---|
| 74 | {directory, "src2", [{file, "yo.erl", "-module(yo)."}]}], |
|---|
| 75 | ok = fixtures: use_tree (Tree, fun can_be_given_several_directories/2). |
|---|
| 76 | |
|---|
| 77 | can_be_given_several_directories (Root, _) -> |
|---|
| 78 | Dirs = [filename: join (Root, D) || D <- ["src1", "src2"]], |
|---|
| 79 | Compiler = spawn_link (compiler, init, [notify_me (), Dirs]), |
|---|
| 80 | Compiler ! check, |
|---|
| 81 | Ms = receive_all (), |
|---|
| 82 | Compiler ! stop, |
|---|
| 83 | [{3, 0, 0}, _, _, {3, 3, 3}, {{binaries, _}, _}] = Ms, |
|---|
| 84 | ok. |
|---|
| 85 | |
|---|
| 86 | can_be_given_include_directories () -> |
|---|
| 87 | Src = "-module(inc).\n-include(\"inc.hrl\").", |
|---|
| 88 | Inc = "-define(yo,\"YO\").", |
|---|
| 89 | Tree = [{directory, "include", [{file, "inc.hrl", Inc}]}, |
|---|
| 90 | {directory, "src", [{file, "inc.erl", Src}]}], |
|---|
| 91 | ok = fixtures: use_tree (Tree, fun include_no_directory/2), |
|---|
| 92 | ok = fixtures: use_tree (Tree, fun include_with_directory/2). |
|---|
| 93 | |
|---|
| 94 | adds_root_to_include_path () -> |
|---|
| 95 | Tree = |
|---|
| 96 | [{directory, "include", |
|---|
| 97 | [{file, "eg_include.hrl", "-define(eg_macro,ok)."}]}, |
|---|
| 98 | {directory, "src", |
|---|
| 99 | [{file, "eg_code_with_include.erl", |
|---|
| 100 | ["-module (eg_code_with_include).", |
|---|
| 101 | "-test (ok).", |
|---|
| 102 | "-export ([ok/0]).", |
|---|
| 103 | "-include (\"include/eg_include.hrl\").", |
|---|
| 104 | "ok () ->", |
|---|
| 105 | " ok = ?eg_macro."]}]}], |
|---|
| 106 | ok = fixtures: use_tree (Tree, fun adds_root_to_include_path/2). |
|---|
| 107 | |
|---|
| 108 | adds_root_to_include_path (Root, _) -> |
|---|
| 109 | Compiler = spawn_link (compiler, init, [notify_me (), [Root]]), |
|---|
| 110 | Compiler ! check, |
|---|
| 111 | Ms = receive_all (), |
|---|
| 112 | Compiler ! stop, |
|---|
| 113 | [{1, 0, 0}, {1, 1, 1}, {{binaries, _}, _}] = Ms, |
|---|
| 114 | ok. |
|---|
| 115 | |
|---|
| 116 | discovers_hrl_files_and_includes_their_path () -> |
|---|
| 117 | Tree = [{directory, "include", |
|---|
| 118 | [{file, "eg_include.hrl", "-define(eg_macro,ok)."}]}, |
|---|
| 119 | {directory, "src", |
|---|
| 120 | [{file, "eg_code_with_include.erl", |
|---|
| 121 | ["-module (eg_code_with_include).", |
|---|
| 122 | "-test (ok).", |
|---|
| 123 | "-export ([ok/0]).", |
|---|
| 124 | "-include (\"eg_include.hrl\").", |
|---|
| 125 | "ok () ->", |
|---|
| 126 | " ok = ?eg_macro."]}]}], |
|---|
| 127 | ok = fixtures: use_tree (Tree, fun discovers_hrl/2). |
|---|
| 128 | |
|---|
| 129 | discovers_hrl (Root, _) -> |
|---|
| 130 | Compiler = spawn_link (compiler, init, [notify_me (), [Root]]), |
|---|
| 131 | Compiler ! check, |
|---|
| 132 | Ms = receive_all (), |
|---|
| 133 | Compiler ! stop, |
|---|
| 134 | [{1, 0, 0}, {1, 1, 1}, _] = Ms, |
|---|
| 135 | ok. |
|---|
| 136 | |
|---|
| 137 | provides_binaries_when_all_compiled (Root, _) -> |
|---|
| 138 | Compiler = spawn_link (compiler, init, [notify_me (), [Root]]), |
|---|
| 139 | Compiler ! check, |
|---|
| 140 | Ms = receive_all (), |
|---|
| 141 | Compiler ! stop, |
|---|
| 142 | ok = check (Ms, unknown), |
|---|
| 143 | {{binaries, Bs}, _} = lists: last (Ms), |
|---|
| 144 | Chunks = [ beam_lib: chunks (B, [exports]) || B <- Bs], |
|---|
| 145 | Modules = [M || {ok, {M, [{exports, [_, _, {run, 0}]}]}} <- Chunks], |
|---|
| 146 | [gdbye, hello] = lists: sort (Modules), |
|---|
| 147 | ok. |
|---|
| 148 | |
|---|
| 149 | provides_all_binaries_not_just_latest_when_all_compiled (Root, [_, File]) -> |
|---|
| 150 | Compiler = spawn_link (compiler, init, [notify_me (), [Root]]), |
|---|
| 151 | Compiler ! check, |
|---|
| 152 | [{2, 0, 0}, _, _, {2, 2, 1}] = receive_all (), |
|---|
| 153 | {file, Filename, _} = File, |
|---|
| 154 | Bin = list_to_binary ("-module(gdbye). -export([run/0]). run()->gdbye."), |
|---|
| 155 | ok = file: write_file (filename: join (Root, Filename), Bin), |
|---|
| 156 | Compiler ! check, |
|---|
| 157 | [{2, 1, 1}, {2, 2, 2}, {{binaries, [_, _]}, _}] = receive_all (), |
|---|
| 158 | ok. |
|---|
| 159 | |
|---|
| 160 | recompiles_minimally_after_change (Root, _) -> |
|---|
| 161 | Compiler = spawn_link (compiler, init, [notify_me (), [Root]]), |
|---|
| 162 | Compiler ! check, |
|---|
| 163 | receive_all (), |
|---|
| 164 | File = filename: join (Root, "hello.erl"), |
|---|
| 165 | Code = "-module(hello). -export([run/0]). run()->hello_howdy.", |
|---|
| 166 | Bin = list_to_binary (Code), |
|---|
| 167 | ok = file: write_file (File, Bin), |
|---|
| 168 | Compiler ! check, |
|---|
| 169 | [{2, 1, 1}, {2, 2, 2}, {{binaries, [B]}, _}] = receive_all (), |
|---|
| 170 | Compiler ! stop, |
|---|
| 171 | {ok, {hello, _}} = beam_lib: chunks (B, [exports]), |
|---|
| 172 | ok. |
|---|
| 173 | |
|---|
| 174 | can_start_empty_and_add_files (Root, []) -> |
|---|
| 175 | Compiler = spawn_link (compiler, init, [notify_me (), [Root]]), |
|---|
| 176 | Compiler ! check, |
|---|
| 177 | [timeout] = receive_all (), |
|---|
| 178 | File = filename: join (Root, "hello.erl"), |
|---|
| 179 | Bin = list_to_binary ("-module(hello). -export([run/0]). run()->hello."), |
|---|
| 180 | ok = file: write_file (File, Bin), |
|---|
| 181 | Compiler ! check, |
|---|
| 182 | [{1, 0, 0}, {1, 1, 1}, {{binaries, [_]}, _}] = receive_all (), |
|---|
| 183 | Compiler ! stop, |
|---|
| 184 | ok. |
|---|
| 185 | |
|---|
| 186 | notifies_removed_files (Root, [F, _]) -> |
|---|
| 187 | Compiler = spawn_link (compiler, init, [notify_me (), [Root]]), |
|---|
| 188 | Compiler ! check, |
|---|
| 189 | receive_all (), |
|---|
| 190 | {file, File, _} = F, |
|---|
| 191 | ok = file: delete (filename: join (Root, File)), |
|---|
| 192 | Compiler ! check, |
|---|
| 193 | [{1, 1, 1}, {{binaries, []}, {removed, [hello]}}] = receive_all (), |
|---|
| 194 | Compiler ! {self (), stop}, |
|---|
| 195 | ok. |
|---|
| 196 | |
|---|
| 197 | notifies_removed_files_even_if_never_compiled (Root, [F]) -> |
|---|
| 198 | Compiler = spawn_link (compiler, init, [notify_me (), [Root]]), |
|---|
| 199 | Compiler ! check, |
|---|
| 200 | [{1, 0, 0}, {_, _}, {1, 1, 0}] = receive_all (), |
|---|
| 201 | {file, File, _} = F, |
|---|
| 202 | Filename = filename: join (Root, File), |
|---|
| 203 | Module = modules: module_name (Filename), |
|---|
| 204 | ok = file: delete (Filename), |
|---|
| 205 | Compiler ! check, |
|---|
| 206 | [{0, 0, 0}, {{binaries, []}, {removed, [Module]}}] = receive_all (), |
|---|
| 207 | Compiler ! {self (), stop}, |
|---|
| 208 | ok. |
|---|
| 209 | |
|---|
| 210 | include_no_directory (Root, _) -> |
|---|
| 211 | Src = filename: join (Root, "src"), |
|---|
| 212 | Compiler = spawn_link (compiler, init, [notify_me (), [Src]]), |
|---|
| 213 | Compiler ! check, |
|---|
| 214 | [{1, 0, 0}, {_, _}, {1, 1, 0}] = receive_all (), |
|---|
| 215 | Compiler ! {self (), stop}, |
|---|
| 216 | ok. |
|---|
| 217 | |
|---|
| 218 | include_with_directory (Root, _) -> |
|---|
| 219 | Src = filename: join (Root, "src"), |
|---|
| 220 | Inc = filename: join (Root, "include"), |
|---|
| 221 | Compiler = spawn_link (compiler, init, [notify_me (), [Src], [{i, Inc}]]), |
|---|
| 222 | Compiler ! check, |
|---|
| 223 | [{1, 0, 0}, {1, 1, 1}, {{binaries, _}, _}] = receive_all (), |
|---|
| 224 | Compiler ! {self (), stop}, |
|---|
| 225 | ok. |
|---|
| 226 | |
|---|
| 227 | check ([{Total, 0, 0} | _] = Xs, unknown) -> |
|---|
| 228 | check (Xs, Total); |
|---|
| 229 | check ([{Total, M1, N1}, {Total, M2, N2} = X | Xs], Total) |
|---|
| 230 | when M2 == M1+1, N2 == N1 + 1 -> |
|---|
| 231 | check ([X | Xs], Total); |
|---|
| 232 | check ([{Total, M1, N1}, {[], _}, {Total, M2, N2} = X | Xs], Total) |
|---|
| 233 | when M2 == M1+1, N2 == N1 + 1 -> |
|---|
| 234 | check ([X | Xs], Total); |
|---|
| 235 | check ([{Total, M1, N1}, {_, _}, {Total, M2, N2} = X | Xs], Total) |
|---|
| 236 | when M2 == M1+1, N2 == N1 -> |
|---|
| 237 | check ([X | Xs], Total); |
|---|
| 238 | check ([{Total, Total, N}], Total) when N < Total -> |
|---|
| 239 | ok; |
|---|
| 240 | check ([{Total, Total, Total}, {{binaries, _}, {removed, _}}], Total) -> |
|---|
| 241 | ok. |
|---|
| 242 | |
|---|
| 243 | good_erl_tree () -> |
|---|
| 244 | [{file, "hello.erl", "-module(hello). -export([run/0]). run()->hello."}, |
|---|
| 245 | {file, "gdbye.erl", "-module(gdbye). -export([run/0]). run()->gdbye."}]. |
|---|
| 246 | |
|---|
| 247 | bad_erl_tree () -> |
|---|
| 248 | [{file, "hello.erl", "-module(hello). -export([run/0]). run()->hello."}, |
|---|
| 249 | {file, "gdbye.erl", "-module(gdbye) -export([run/0]) run()->gdbye"}]. |
|---|
| 250 | |
|---|
| 251 | notify_me () -> |
|---|
| 252 | Self = self (), |
|---|
| 253 | fun (M) -> Self ! M end. |
|---|
| 254 | |
|---|
| 255 | receive_all () -> |
|---|
| 256 | receive_all ([]). |
|---|
| 257 | |
|---|
| 258 | receive_all (Ms) -> |
|---|
| 259 | receive |
|---|
| 260 | {Total, Total, Total} = Ts -> |
|---|
| 261 | receive {{binaries, _}, {removed, _}} = Bs -> |
|---|
| 262 | lists: reverse ([Bs, Ts | Ms]) |
|---|
| 263 | end; |
|---|
| 264 | {Total, Total, _} = Ts -> |
|---|
| 265 | lists: reverse ([Ts | Ms]); |
|---|
| 266 | M -> |
|---|
| 267 | receive_all ([M | Ms]) |
|---|
| 268 | after 2000 -> lists: reverse ([timeout | Ms]) |
|---|
| 269 | end. |
|---|
| 270 | |
|---|
| 271 | reset_includes_none () -> |
|---|
| 272 | [debug, bla] = compiler: reset_includes (dict: new (), [debug, bla]). |
|---|
| 273 | |
|---|
| 274 | reset_includes_found () -> |
|---|
| 275 | Found = [{"/dir/file.hrl", found}, {"/dir/subdir/file2.hrl", found}], |
|---|
| 276 | New = dict: from_list (Found), |
|---|
| 277 | Options = [debug, bla], |
|---|
| 278 | Expected = [debug, bla, {i, "/dir/subdir"}, {i, "/dir"}], |
|---|
| 279 | Expected = compiler: reset_includes (New, Options). |
|---|
| 280 | |
|---|
| 281 | %% recompiles_includers () -> |
|---|
| 282 | %% Tree = modules_test: includes_tree (), |
|---|
| 283 | %% ok = fixtures: use_tree (Tree, fun recompiles_includers/2). |
|---|
| 284 | |
|---|
| 285 | %% recompiles_includers (Root, _) -> |
|---|
| 286 | %% Compiler = spawn_link (compiler, init, [notify_me (), [Root]]), |
|---|
| 287 | %% Compiler ! check, |
|---|
| 288 | %% [{3, 0, 0}, _, _, {3, 3, 3}, _] = receive_all (), |
|---|
| 289 | %% Include = filename: join (Root, "eg_include.hrl"), |
|---|
| 290 | %% Define = "-define(eg_macro, ko)", |
|---|
| 291 | %% ok = file: write_file (Include, Define), |
|---|
| 292 | %% Compiler ! check, |
|---|
| 293 | %% [{3, 2, 2}, {3, 3, 3}, {{binaries, _}, _}] = receive_all (), |
|---|
| 294 | %% ok. |
|---|