Label `from_root` suffix argument
[hiphop-php.git] / hphp / hack / test / unit / naming / naming_table_tests.ml
blobc6c6ffd4458c5b5134fe30fc7153e66dcfcc2178
1 (*
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
9 *)
11 (* These are basically the same tests as in
12 * test/integration_ml/saved_state/test_naming_table_sqlite_fallback.ml, but
13 * as stripped down to just the basics as possible to make finding the root
14 * cause of test failures easier. *)
16 open Core_kernel
18 module Types_pos_asserter = Asserter.Make_asserter (struct
19 type t = FileInfo.pos * Naming_types.kind_of_type
21 let to_string (pos, type_of_type) =
22 Printf.sprintf
23 "(%s, %d)"
24 (FileInfo.show_pos pos)
25 (Naming_types.kind_of_type_to_enum type_of_type)
27 let is_equal = ( = )
28 end)
30 module Pos_asserter = Asserter.Make_asserter (struct
31 type t = FileInfo.pos
33 let to_string pos = Printf.sprintf "(%s)" (FileInfo.show_pos pos)
35 let is_equal = ( = )
36 end)
38 let files =
40 ("foo.php", {|<?hh
41 class Foo {}
42 |});
43 ("bar.php", {|<?hh
44 function bar(): void {}
45 |});
46 ("baz.php", {|<?hh
47 type Baz = Foo;
48 |});
49 ("qux.php", {|<?hh
50 const int Qux = 5;
51 |});
54 let write_and_parse_test_files () =
55 let files =
56 List.map files ~f:(fun (fn, contents) ->
57 (Relative_path.from_root ~suffix:fn, contents))
59 List.iter files ~f:(fun (fn, contents) ->
60 let fn = Path.make (Relative_path.to_absolute fn) in
61 let dir = Path.dirname fn in
62 Disk.mkdir_p (Path.to_string dir);
63 Disk.write_file ~file:(Path.to_string fn) ~contents);
64 let (file_infos, errors, failed_parsing) =
65 Parsing_service.go
66 None
67 Relative_path.Set.empty
68 ~get_next:(MultiWorker.next None (List.map files ~f:fst))
69 ParserOptions.default
70 ~trace:true
72 if not (Errors.is_empty errors) then (
73 Errors.iter_error_list
74 (fun e ->
75 List.iter (Errors.to_list e) ~f:(fun (pos, msg) ->
76 eprintf "%s: %s\n" (Pos.string (Pos.to_absolute pos)) msg))
77 errors;
78 failwith "Expected no errors from parsing."
80 if failed_parsing <> Relative_path.Set.empty then
81 failwith "Expected all files to pass parsing.";
82 Naming_table.create file_infos
84 let run_naming_table_test f =
85 Tempfile.with_real_tempdir (fun path ->
86 Relative_path.set_path_prefix
87 Relative_path.Root
88 (Path.concat path "root/");
89 let config =
90 SharedMem.
92 global_size = 1024;
93 heap_size = 1024 * 1024;
94 dep_table_pow = 16;
95 hash_table_pow = 10;
96 shm_dirs = [];
97 shm_min_avail = 0;
98 log_level = 0;
99 sample_rate = 0.0;
102 let (_ : SharedMem.handle) = SharedMem.init config ~num_workers:0 in
103 let unbacked_naming_table = write_and_parse_test_files () in
104 let db_name = Path.to_string (Path.concat path "naming_table.sqlite") in
105 let save_results = Naming_table.save unbacked_naming_table db_name in
106 Asserter.Int_asserter.assert_equals
108 Naming_sqlite.(save_results.files_added + save_results.symbols_added)
109 "Expected to add eight rows (four files and four symbols)";
111 let popt = ParserOptions.default in
112 let tcopt = TypecheckerOptions.default in
113 let ctx_for_sqlite_load = Provider_context.empty_for_test ~popt ~tcopt in
114 let backed_naming_table =
115 Naming_table.load_from_sqlite ctx_for_sqlite_load db_name
118 Provider_backend.set_local_memory_backend_with_defaults ();
119 let ctx =
120 Provider_context.empty_for_tool
121 ~popt
122 ~tcopt
123 ~backend:(Provider_backend.get ())
125 (try f ~ctx ~unbacked_naming_table ~backed_naming_table ~db_name
126 with e ->
127 Printf.eprintf
128 "NOTE: backend was local-memory for this exception's test run\n";
129 raise e);
130 Provider_backend.set_shared_memory_backend ();
131 let ctx =
132 Provider_context.empty_for_tool
133 ~popt
134 ~tcopt
135 ~backend:(Provider_backend.get ())
137 (try f ~ctx ~unbacked_naming_table ~backed_naming_table ~db_name
138 with e ->
139 Printf.eprintf
140 "NOTE: backend was shared-memory for this exception's test run\n";
141 raise e);
142 true)
144 let test_get_pos () =
145 run_naming_table_test
146 (fun ~ctx ~unbacked_naming_table:_ ~backed_naming_table:_ ~db_name:_ ->
147 Types_pos_asserter.assert_option_equals
148 (Some
149 ( FileInfo.File
150 (FileInfo.Class, Relative_path.from_root ~suffix:"foo.php"),
151 Naming_types.TClass ))
152 (Naming_provider.get_type_pos_and_kind ctx "\\Foo")
153 "Check for class type";
154 Pos_asserter.assert_option_equals
155 (Some
156 (FileInfo.File
157 (FileInfo.Fun, Relative_path.from_root ~suffix:"bar.php")))
158 (Naming_provider.get_fun_pos ctx "\\bar")
159 "Check for function";
160 Types_pos_asserter.assert_option_equals
161 (Some
162 ( FileInfo.File
163 (FileInfo.Typedef, Relative_path.from_root ~suffix:"baz.php"),
164 Naming_types.TTypedef ))
165 (Naming_provider.get_type_pos_and_kind ctx "\\Baz")
166 "Check for typedef type";
167 Pos_asserter.assert_option_equals
168 (Some
169 (FileInfo.File
170 (FileInfo.Const, Relative_path.from_root ~suffix:"qux.php")))
171 (Naming_provider.get_const_pos ctx "\\Qux")
172 "Check for const")
174 let test_get_canon_name () =
175 run_naming_table_test
176 (fun ~ctx ~unbacked_naming_table:_ ~backed_naming_table:_ ~db_name:_ ->
177 (* Since we're parsing but not naming, the canon heap must fall back to the
178 files on disk, which is the situation we'd be in when loading from a
179 saved state. *)
180 Asserter.String_asserter.assert_option_equals
181 (Some "\\Foo")
182 (Naming_provider.get_type_canon_name ctx "\\foo")
183 "Check for class canon name";
184 Asserter.String_asserter.assert_option_equals
185 (Some "\\bar")
186 (Naming_provider.get_fun_canon_name ctx "\\bar")
187 "Check for function canon name lowercase";
188 Asserter.String_asserter.assert_option_equals
189 (Some "\\bar")
190 (Naming_provider.get_fun_canon_name ctx "\\BAR")
191 "Check for function canon name uppercase";
192 Asserter.String_asserter.assert_option_equals
193 (Some "\\Baz")
194 (Naming_provider.get_type_canon_name ctx "\\baz")
195 "Check for typedef canon name")
197 let test_remove () =
198 run_naming_table_test
199 (fun ~ctx:_ ~unbacked_naming_table ~backed_naming_table ~db_name:_ ->
200 let foo_path = Relative_path.from_root ~suffix:"foo.php" in
201 assert (
202 Naming_table.get_file_info unbacked_naming_table foo_path
203 |> Option.is_some );
204 let unbacked_naming_table =
205 Naming_table.remove unbacked_naming_table foo_path
207 assert (
208 Naming_table.get_file_info unbacked_naming_table foo_path
209 |> Option.is_none );
211 assert (
212 Naming_table.get_file_info backed_naming_table foo_path
213 |> Option.is_some );
214 let backed_naming_table =
215 Naming_table.remove backed_naming_table foo_path
217 assert (
218 Naming_table.get_file_info backed_naming_table foo_path
219 |> Option.is_none ))
221 let test_get_sqlite_paths () =
222 run_naming_table_test
223 (fun ~ctx:_ ~unbacked_naming_table:_ ~backed_naming_table ~db_name ->
224 Asserter.String_asserter.assert_option_equals
225 (Some db_name)
226 (Naming_table.get_reverse_naming_fallback_path ())
227 "get_reverse_naming_fallback_path should return the expected value";
229 Asserter.String_asserter.assert_option_equals
230 (Some db_name)
231 (Naming_table.get_forward_naming_fallback_path backed_naming_table)
232 "get_forward_naming_fallback_path should return the expected value")
234 let test_local_changes () =
235 run_naming_table_test
236 (fun ~ctx ~unbacked_naming_table:_ ~backed_naming_table ~db_name ->
237 let a_name = "CONST_IN_A" in
239 let a_file = Relative_path.from_root ~suffix:"a.php" in
240 let a_pos = FileInfo.File (FileInfo.Const, a_file) in
241 let a_file_info =
242 FileInfo.{ FileInfo.empty_t with consts = [(a_pos, a_name)] }
244 let backed_naming_table =
245 Naming_table.update backed_naming_table a_file a_file_info
247 let changes_since_baseline_path = "/tmp/base_plus_changes" in
248 Naming_table.save_changes_since_baseline
249 backed_naming_table
250 changes_since_baseline_path;
251 let (changes_since_baseline : Naming_table.changes_since_baseline) =
252 Marshal.from_string (Disk.cat changes_since_baseline_path) 0
254 Asserter.Relative_path_asserter.assert_list_equals
255 [a_file]
256 (Naming_table.get_files_changed_since_baseline changes_since_baseline)
257 "Expected files changed since baseline to be correct";
258 let backed_naming_table' =
259 Naming_table.load_from_sqlite_with_changes_since_baseline
261 changes_since_baseline
262 db_name
264 let a_file_info' =
265 Option.value_exn
266 (Naming_table.get_file_info backed_naming_table' a_file)
268 Asserter.Bool_asserter.assert_equals
269 true
270 (a_file_info = a_file_info')
271 "Expected file info to be found in the naming table";
273 let a_pos' =
274 Option.value_exn (Naming_provider.get_const_pos ctx a_name)
276 Asserter.Bool_asserter.assert_equals
277 true
278 (a_pos = a_pos')
279 "Expected position of constant to be found in the naming table")
281 let test_context_changes_consts () =
282 run_naming_table_test
283 (fun ~ctx ~unbacked_naming_table:_ ~backed_naming_table:_ ~db_name:_ ->
284 let (ctx, _entry) =
285 Provider_context.add_or_overwrite_entry_contents
286 ~ctx
287 ~path:(Relative_path.from_root ~suffix:"foo.php")
288 ~contents:{|<?hh
289 class New_qux {}
292 let (ctx, _entry) =
293 Provider_context.add_or_overwrite_entry_contents
294 ~ctx
295 ~path:(Relative_path.from_root ~suffix:"qux.php")
296 ~contents:{|<?hh
297 const int New_qux = 5;
300 Asserter.Relative_path_asserter.assert_option_equals
301 (Some (Relative_path.from_root ~suffix:"qux.php"))
302 (Naming_provider.get_const_path ctx "\\New_qux")
303 "New const in context should be visible";
304 Asserter.Relative_path_asserter.assert_option_equals
305 None
306 (Naming_provider.get_const_path ctx "\\Qux")
307 "Old, deleted const in context should NOT be visible")
309 let test_context_changes_funs () =
310 run_naming_table_test
311 (fun ~ctx ~unbacked_naming_table:_ ~backed_naming_table:_ ~db_name:_ ->
312 let (ctx, _entry) =
313 Provider_context.add_or_overwrite_entry_contents
314 ~ctx
315 ~path:(Relative_path.from_root ~suffix:"foo.php")
316 ~contents:{|<?hh
317 class bar {}
320 let (ctx, _entry) =
321 Provider_context.add_or_overwrite_entry_contents
322 ~ctx
323 ~path:(Relative_path.from_root ~suffix:"bar.php")
324 ~contents:{|<?hh
325 function new_bar(): void {}
328 Asserter.Relative_path_asserter.assert_option_equals
329 (Some (Relative_path.from_root ~suffix:"bar.php"))
330 (Naming_provider.get_fun_path ctx "\\new_bar")
331 "New function in context should be visible";
332 Asserter.Relative_path_asserter.assert_option_equals
333 None
334 (Naming_provider.get_fun_path ctx "\\bar")
335 "Old, deleted function in context should NOT be visible";
337 Asserter.String_asserter.assert_option_equals
338 (Some "\\new_bar")
339 (Naming_provider.get_fun_canon_name ctx "\\NeW_bAr")
340 "New function in context should be accessible by canon name";
342 (* NB: under shared-memory provider, this doesn't hold true if we made
343 a call to `get_fun_canon_name` before we added the context entry, as
344 the result will be cached. The caller is expected to have manually
345 removed any old reverse naming table entries manually in that case. *)
346 Asserter.String_asserter.assert_option_equals
347 None
348 (Naming_provider.get_fun_canon_name ctx "\\BAR")
349 "Old function in context should NOT be accessible by canon name")
351 let test_context_changes_classes () =
352 run_naming_table_test
353 (fun ~ctx ~unbacked_naming_table:_ ~backed_naming_table:_ ~db_name:_ ->
354 let (ctx, _entry) =
355 Provider_context.add_or_overwrite_entry_contents
356 ~ctx
357 ~path:(Relative_path.from_root ~suffix:"foo.php")
358 ~contents:{|<?hh
359 class NewFoo {}
362 Asserter.Relative_path_asserter.assert_option_equals
363 (Some (Relative_path.from_root ~suffix:"foo.php"))
364 (Naming_provider.get_class_path ctx "\\NewFoo")
365 "New class in context should be visible";
366 Asserter.Relative_path_asserter.assert_option_equals
367 None
368 (Naming_provider.get_class_path ctx "\\Foo")
369 "Old class in context should NOT be visible";
371 Asserter.String_asserter.assert_option_equals
372 (Some "\\NewFoo")
373 (Naming_provider.get_type_canon_name ctx "\\NEWFOO")
374 "New class in context should be accessible by canon name";
376 (* NB: under shared-memory provider, this doesn't hold true if we made
377 a call to `get_type_canon_name` before we added the context entry, as
378 the result will be cached. The caller is expected to have manually
379 removed any old reverse naming table entries manually in that case. *)
380 Asserter.String_asserter.assert_option_equals
381 None
382 (Naming_provider.get_type_canon_name ctx "\\FOO")
383 "Old class in context should NOT be accessible by canon name")
385 let test_context_changes_typedefs () =
386 run_naming_table_test
387 (fun ~ctx ~unbacked_naming_table:_ ~backed_naming_table:_ ~db_name:_ ->
388 let (ctx, _entry) =
389 Provider_context.add_or_overwrite_entry_contents
390 ~ctx
391 ~path:(Relative_path.from_root ~suffix:"baz.php")
392 ~contents:{|<?hh
393 type NewBaz = Foo;
396 Asserter.Relative_path_asserter.assert_option_equals
397 (Some (Relative_path.from_root ~suffix:"baz.php"))
398 (Naming_provider.get_typedef_path ctx "\\NewBaz")
399 "New typedef in context should be visible";
400 Asserter.Relative_path_asserter.assert_option_equals
401 None
402 (Naming_provider.get_typedef_path ctx "\\Baz")
403 "Old typedef in context should NOT be visible";
405 Asserter.String_asserter.assert_option_equals
406 (Some "\\NewBaz")
407 (Naming_provider.get_type_canon_name ctx "\\NEWBAZ")
408 "New typedef in context should be accessible by canon name";
410 (* NB: under shared-memory provider, this doesn't hold true if we made
411 a call to `get_type_canon_name` before we added the context entry, as
412 the result will be cached. The caller is expected to have manually
413 removed any old reverse naming table entries manually in that case. *)
414 Asserter.String_asserter.assert_option_equals
415 None
416 (Naming_provider.get_type_canon_name ctx "\\BAZ")
417 "Old typedef in context should NOT be accessible by canon name")
419 let () =
420 let config =
421 SharedMem.
423 global_size = 1024;
424 heap_size = 1024 * 1024;
425 dep_table_pow = 16;
426 hash_table_pow = 10;
427 shm_dirs = [];
428 shm_min_avail = 0;
429 log_level = 0;
430 sample_rate = 0.0;
433 let (_ : SharedMem.handle) = SharedMem.init config ~num_workers:0 in
434 Unit_test.run_all
436 ("test_get_pos", test_get_pos);
437 ("test_get_canon_name", test_get_canon_name);
438 ("test_remove", test_remove);
439 ("test_get_sqlite_paths", test_get_sqlite_paths);
440 ("test_local_changes", test_local_changes);
441 ("test_context_changes_consts", test_context_changes_consts);
442 ("test_context_changes_funs", test_context_changes_funs);
443 ("test_context_changes_classes", test_context_changes_classes);
444 ("test_context_changes_typedefs", test_context_changes_typedefs);