Provider_context.add_entry_from_file_contents -> add_entry_from_contents
[hiphop-php.git] / hphp / hack / test / unit / naming / naming_table_tests.ml
blob93239ebbb558c0ed2e76bda7f62bdd88206b1fa8
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 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 (FileInfo.Class, Relative_path.from_root "foo.php"),
150 Naming_types.TClass ))
151 (Naming_provider.get_type_pos_and_kind ctx "\\Foo")
152 "Check for class type";
153 Pos_asserter.assert_option_equals
154 (Some (FileInfo.File (FileInfo.Fun, Relative_path.from_root "bar.php")))
155 (Naming_provider.get_fun_pos ctx "\\bar")
156 "Check for function";
157 Types_pos_asserter.assert_option_equals
158 (Some
159 ( FileInfo.File (FileInfo.Typedef, Relative_path.from_root "baz.php"),
160 Naming_types.TTypedef ))
161 (Naming_provider.get_type_pos_and_kind ctx "\\Baz")
162 "Check for typedef type";
163 Pos_asserter.assert_option_equals
164 (Some
165 (FileInfo.File (FileInfo.Const, Relative_path.from_root "qux.php")))
166 (Naming_provider.get_const_pos ctx "\\Qux")
167 "Check for const")
169 let test_get_canon_name () =
170 run_naming_table_test
171 (fun ~ctx ~unbacked_naming_table:_ ~backed_naming_table:_ ~db_name:_ ->
172 (* Since we're parsing but not naming, the canon heap must fall back to the
173 files on disk, which is the situation we'd be in when loading from a
174 saved state. *)
175 Asserter.String_asserter.assert_option_equals
176 (Some "\\Foo")
177 (Naming_provider.get_type_canon_name ctx "\\foo")
178 "Check for class canon name";
179 Asserter.String_asserter.assert_option_equals
180 (Some "\\bar")
181 (Naming_provider.get_fun_canon_name ctx "\\bar")
182 "Check for function canon name lowercase";
183 Asserter.String_asserter.assert_option_equals
184 (Some "\\bar")
185 (Naming_provider.get_fun_canon_name ctx "\\BAR")
186 "Check for function canon name uppercase";
187 Asserter.String_asserter.assert_option_equals
188 (Some "\\Baz")
189 (Naming_provider.get_type_canon_name ctx "\\baz")
190 "Check for typedef canon name")
192 let test_remove () =
193 run_naming_table_test
194 (fun ~ctx:_ ~unbacked_naming_table ~backed_naming_table ~db_name:_ ->
195 let foo_path = Relative_path.from_root "foo.php" in
196 assert (
197 Naming_table.get_file_info unbacked_naming_table foo_path
198 |> Option.is_some );
199 let unbacked_naming_table =
200 Naming_table.remove unbacked_naming_table foo_path
202 assert (
203 Naming_table.get_file_info unbacked_naming_table foo_path
204 |> Option.is_none );
206 assert (
207 Naming_table.get_file_info backed_naming_table foo_path
208 |> Option.is_some );
209 let backed_naming_table =
210 Naming_table.remove backed_naming_table foo_path
212 assert (
213 Naming_table.get_file_info backed_naming_table foo_path
214 |> Option.is_none ))
216 let test_get_sqlite_paths () =
217 run_naming_table_test
218 (fun ~ctx:_ ~unbacked_naming_table:_ ~backed_naming_table ~db_name ->
219 Asserter.String_asserter.assert_option_equals
220 (Some db_name)
221 (Naming_table.get_reverse_naming_fallback_path ())
222 "get_reverse_naming_fallback_path should return the expected value";
224 Asserter.String_asserter.assert_option_equals
225 (Some db_name)
226 (Naming_table.get_forward_naming_fallback_path backed_naming_table)
227 "get_forward_naming_fallback_path should return the expected value")
229 let test_local_changes () =
230 run_naming_table_test
231 (fun ~ctx ~unbacked_naming_table:_ ~backed_naming_table ~db_name ->
232 let a_name = "CONST_IN_A" in
234 let a_file = Relative_path.from_root "a.php" in
235 let a_pos = FileInfo.File (FileInfo.Const, a_file) in
236 let a_file_info =
237 FileInfo.{ FileInfo.empty_t with consts = [(a_pos, a_name)] }
239 let backed_naming_table =
240 Naming_table.update backed_naming_table a_file a_file_info
242 let changes_since_baseline_path = "/tmp/base_plus_changes" in
243 Naming_table.save_changes_since_baseline
244 backed_naming_table
245 changes_since_baseline_path;
246 let (changes_since_baseline : Naming_table.changes_since_baseline) =
247 Marshal.from_string (Disk.cat changes_since_baseline_path) 0
249 Asserter.Relative_path_asserter.assert_list_equals
250 [a_file]
251 (Naming_table.get_files_changed_since_baseline changes_since_baseline)
252 "Expected files changed since baseline to be correct";
253 let backed_naming_table' =
254 Naming_table.load_from_sqlite_with_changes_since_baseline
256 changes_since_baseline
257 db_name
259 let a_file_info' =
260 Option.value_exn
261 (Naming_table.get_file_info backed_naming_table' a_file)
263 Asserter.Bool_asserter.assert_equals
264 true
265 (a_file_info = a_file_info')
266 "Expected file info to be found in the naming table";
268 let a_pos' =
269 Option.value_exn (Naming_provider.get_const_pos ctx a_name)
271 Asserter.Bool_asserter.assert_equals
272 true
273 (a_pos = a_pos')
274 "Expected position of constant to be found in the naming table")
276 let test_context_changes_consts () =
277 run_naming_table_test
278 (fun ~ctx ~unbacked_naming_table:_ ~backed_naming_table:_ ~db_name:_ ->
279 let (ctx, _entry) =
280 Provider_context.add_or_overwrite_entry_contents
281 ~ctx
282 ~path:(Relative_path.from_root "foo.php")
283 ~contents:{|<?hh
284 class New_qux {}
287 let (ctx, _entry) =
288 Provider_context.add_or_overwrite_entry_contents
289 ~ctx
290 ~path:(Relative_path.from_root "qux.php")
291 ~contents:{|<?hh
292 const int New_qux = 5;
295 Asserter.Relative_path_asserter.assert_option_equals
296 (Some (Relative_path.from_root "qux.php"))
297 (Naming_provider.get_const_path ctx "\\New_qux")
298 "New const in context should be visible";
299 Asserter.Relative_path_asserter.assert_option_equals
300 None
301 (Naming_provider.get_const_path ctx "\\Qux")
302 "Old, deleted const in context should NOT be visible")
304 let test_context_changes_funs () =
305 run_naming_table_test
306 (fun ~ctx ~unbacked_naming_table:_ ~backed_naming_table:_ ~db_name:_ ->
307 let (ctx, _entry) =
308 Provider_context.add_or_overwrite_entry_contents
309 ~ctx
310 ~path:(Relative_path.from_root "foo.php")
311 ~contents:{|<?hh
312 class bar {}
315 let (ctx, _entry) =
316 Provider_context.add_or_overwrite_entry_contents
317 ~ctx
318 ~path:(Relative_path.from_root "bar.php")
319 ~contents:{|<?hh
320 function new_bar(): void {}
323 Asserter.Relative_path_asserter.assert_option_equals
324 (Some (Relative_path.from_root "bar.php"))
325 (Naming_provider.get_fun_path ctx "\\new_bar")
326 "New function in context should be visible";
327 Asserter.Relative_path_asserter.assert_option_equals
328 None
329 (Naming_provider.get_fun_path ctx "\\bar")
330 "Old, deleted function in context should NOT be visible";
332 Asserter.String_asserter.assert_option_equals
333 (Some "\\new_bar")
334 (Naming_provider.get_fun_canon_name ctx "\\NeW_bAr")
335 "New function in context should be accessible by canon name";
337 (* NB: under shared-memory provider, this doesn't hold true if we made
338 a call to `get_fun_canon_name` before we added the context entry, as
339 the result will be cached. The caller is expected to have manually
340 removed any old reverse naming table entries manually in that case. *)
341 Asserter.String_asserter.assert_option_equals
342 None
343 (Naming_provider.get_fun_canon_name ctx "\\BAR")
344 "Old function in context should NOT be accessible by canon name")
346 let test_context_changes_classes () =
347 run_naming_table_test
348 (fun ~ctx ~unbacked_naming_table:_ ~backed_naming_table:_ ~db_name:_ ->
349 let (ctx, _entry) =
350 Provider_context.add_or_overwrite_entry_contents
351 ~ctx
352 ~path:(Relative_path.from_root "foo.php")
353 ~contents:{|<?hh
354 class NewFoo {}
357 Asserter.Relative_path_asserter.assert_option_equals
358 (Some (Relative_path.from_root "foo.php"))
359 (Naming_provider.get_class_path ctx "\\NewFoo")
360 "New class in context should be visible";
361 Asserter.Relative_path_asserter.assert_option_equals
362 None
363 (Naming_provider.get_class_path ctx "\\Foo")
364 "Old class in context should NOT be visible";
366 Asserter.String_asserter.assert_option_equals
367 (Some "\\NewFoo")
368 (Naming_provider.get_type_canon_name ctx "\\NEWFOO")
369 "New class in context should be accessible by canon name";
371 (* NB: under shared-memory provider, this doesn't hold true if we made
372 a call to `get_type_canon_name` before we added the context entry, as
373 the result will be cached. The caller is expected to have manually
374 removed any old reverse naming table entries manually in that case. *)
375 Asserter.String_asserter.assert_option_equals
376 None
377 (Naming_provider.get_type_canon_name ctx "\\FOO")
378 "Old class in context should NOT be accessible by canon name")
380 let test_context_changes_typedefs () =
381 run_naming_table_test
382 (fun ~ctx ~unbacked_naming_table:_ ~backed_naming_table:_ ~db_name:_ ->
383 let (ctx, _entry) =
384 Provider_context.add_or_overwrite_entry_contents
385 ~ctx
386 ~path:(Relative_path.from_root "baz.php")
387 ~contents:{|<?hh
388 type NewBaz = Foo;
391 Asserter.Relative_path_asserter.assert_option_equals
392 (Some (Relative_path.from_root "baz.php"))
393 (Naming_provider.get_typedef_path ctx "\\NewBaz")
394 "New typedef in context should be visible";
395 Asserter.Relative_path_asserter.assert_option_equals
396 None
397 (Naming_provider.get_typedef_path ctx "\\Baz")
398 "Old typedef in context should NOT be visible";
400 Asserter.String_asserter.assert_option_equals
401 (Some "\\NewBaz")
402 (Naming_provider.get_type_canon_name ctx "\\NEWBAZ")
403 "New typedef in context should be accessible by canon name";
405 (* NB: under shared-memory provider, this doesn't hold true if we made
406 a call to `get_type_canon_name` before we added the context entry, as
407 the result will be cached. The caller is expected to have manually
408 removed any old reverse naming table entries manually in that case. *)
409 Asserter.String_asserter.assert_option_equals
410 None
411 (Naming_provider.get_type_canon_name ctx "\\BAZ")
412 "Old typedef in context should NOT be accessible by canon name")
414 let () =
415 let config =
416 SharedMem.
418 global_size = 1024;
419 heap_size = 1024 * 1024;
420 dep_table_pow = 16;
421 hash_table_pow = 10;
422 shm_dirs = [];
423 shm_min_avail = 0;
424 log_level = 0;
425 sample_rate = 0.0;
428 let (_ : SharedMem.handle) = SharedMem.init config ~num_workers:0 in
429 Unit_test.run_all
431 ("test_get_pos", test_get_pos);
432 ("test_get_canon_name", test_get_canon_name);
433 ("test_remove", test_remove);
434 ("test_get_sqlite_paths", test_get_sqlite_paths);
435 ("test_local_changes", test_local_changes);
436 ("test_context_changes_consts", test_context_changes_consts);
437 ("test_context_changes_funs", test_context_changes_funs);
438 ("test_context_changes_classes", test_context_changes_classes);
439 ("test_context_changes_typedefs", test_context_changes_typedefs);