2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
13 let tcopt_with_defer =
14 GlobalOptions.{ default
with tco_defer_class_declaration_threshold
= Some
1 }
16 let test_process_data =
20 server_specific_files
=
21 { ServerCommandTypes.server_finale_file
= "2758734.fin" };
24 out_fds
= [("default", Unix.stdout
)];
25 last_request_handoff
= ref 0.0;
28 let test_dmesg_parser () =
31 "[3034339.262439] Out of memory: Kill process 2758734 (hh_server) score 253 or sacrifice child";
34 Sys_utils.For_test.find_oom_in_dmesg_output
35 test_process_data.ServerProcess.pid
39 let ensure_count (count
: int) : unit =
40 let deferred = Deferred_decl.get_deferments
() in
41 Asserter.Int_asserter.assert_equals
43 (List.length
deferred)
44 "The number of deferred items should match the expected value"
46 let test_deferred_decl_add () =
49 ~declaration_threshold_opt
:None
50 ~memory_mb_threshold_opt
:None
;
53 Deferred_decl.add_deferment
54 ~d
:(Relative_path.create
Relative_path.Dummy
"foo", "\\Foo");
57 Deferred_decl.add_deferment
58 ~d
:(Relative_path.create
Relative_path.Dummy
"foo", "\\Foo");
61 Deferred_decl.add_deferment
62 ~d
:(Relative_path.create
Relative_path.Dummy
"bar", "\\Bar");
67 ~declaration_threshold_opt
:None
68 ~memory_mb_threshold_opt
:None
;
73 let ensure_threshold ~
(threshold
: int) ~
(limit
: int) ~
(expected
: int) : unit
77 ~declaration_threshold_opt
:(Some threshold
)
78 ~memory_mb_threshold_opt
:None
;
81 let deferred_count = ref 0 in
83 let path = Printf.sprintf
"foo-%d" i
in
84 let relative_path = Relative_path.create
Relative_path.Dummy
path in
86 Deferred_decl.raise_if_should_defer ~deferment
:(relative_path, "\\Foo");
87 Deferred_decl.increment_counter
()
88 with Deferred_decl.Defer
(d
, _
) ->
89 Asserter.Bool_asserter.assert_equals
93 "We should have reached the threshold %d, i=%d"
96 Asserter.String_asserter.assert_equals
97 (Relative_path.suffix d
)
99 "The deferred path should be the last one we saw";
100 deferred_count := !deferred_count + 1
103 Asserter.Int_asserter.assert_equals
107 "Unexpected deferred count; threshold: %d; limit: %d; expected: %d"
112 let test_deferred_decl_should_defer () =
113 ensure_threshold ~threshold
:0 ~limit
:1 ~expected
:1;
114 ensure_threshold ~threshold
:1 ~limit
:2 ~expected
:1;
115 ensure_threshold ~threshold
:2 ~limit
:1 ~expected
:0;
116 ensure_threshold ~threshold
:1 ~limit
:5 ~expected
:4;
120 (* In this test, we wish to establish that we enable deferring type checking
121 for files that have undeclared dependencies, UNLESS we've already deferred
122 those files a certain number of times. *)
123 let test_process_file_deferring () =
124 let { Common_setup.ctx
; foo_path
; _
} =
125 Common_setup.setup ~sqlite
:false tcopt_with_defer
127 let file = Typing_service_types.{ path = foo_path
; deferred_count = 0 } in
128 let dynamic_view_files = Relative_path.Set.empty
in
129 let errors = Errors.empty
in
131 (* Finally, this is what all the setup was for: process this file *)
132 Decl_counters.set_mode
Typing_service_types.DeclingTopCounts
;
133 let prev_counter_state = Counters.reset
() in
134 let { Typing_check_service.deferred_decls
; _
} =
135 Typing_check_service.process_file
dynamic_view_files ctx
errors file
137 let counters = Counters.get_counters
() in
138 Counters.restore_state
prev_counter_state;
139 Asserter.Int_asserter.assert_equals
141 (List.length deferred_decls
)
142 "Should be this many deferred_decls";
144 (* this test doesn't write back to cache, so num of decl_fetches isn't solid *)
145 Asserter.Bool_asserter.assert_equals
147 (Telemetry_test_utils.int_exn
counters "decling.count" > 0)
148 "Should be at least one decl fetched";
150 (* Validate the declare file computation *)
152 List.exists deferred_decls ~f
:(fun (deferred_file
, _
) ->
153 Asserter.String_asserter.assert_equals
155 (Relative_path.suffix deferred_file
)
156 "Declare path must match the expected";
159 Asserter.Bool_asserter.assert_equals
162 "Should have found the declare file computation";
166 (* This test verifies that the deferral/counting machinery works for
167 ProviderUtils.compute_tast_and_errors_unquarantined. *)
168 let test_compute_tast_counting () =
169 let { Common_setup.ctx
; foo_path
; foo_contents
; _
} =
170 Common_setup.setup ~sqlite
:false tcopt_with_defer
174 Provider_context.add_or_overwrite_entry_contents
177 ~contents
:foo_contents
179 let { Tast_provider.Compute_tast_and_errors.telemetry
; _
} =
180 Tast_provider.compute_tast_and_errors_unquarantined ~ctx ~entry
183 Asserter.Int_asserter.assert_equals
185 (Telemetry_test_utils.int_exn telemetry
"decling.count")
186 "There should be this many decling_count for shared_mem provider";
187 Asserter.Int_asserter.assert_equals
189 (Telemetry_test_utils.int_exn telemetry
"disk_cat.count")
190 "There should be 0 disk_cat_count for shared_mem provider";
192 (* Now try the same with local_memory backend *)
195 Provider_backend.set_local_memory_backend_with_defaults
())
197 (* restore it back to shared_mem for the rest of the tests *)
198 Provider_backend.set_shared_memory_backend
())
201 Provider_context.empty_for_tool
202 ~popt
:ParserOptions.default
203 ~tcopt
:TypecheckerOptions.default
204 ~backend
:(Provider_backend.get
())
205 ~deps_mode
:Typing_deps_mode.SQLiteMode
208 Provider_context.add_entry_if_missing ~
ctx ~
path:foo_path
210 let { Tast_provider.Compute_tast_and_errors.telemetry
; _
} =
211 Tast_provider.compute_tast_and_errors_unquarantined ~
ctx ~entry
213 Asserter.Int_asserter.assert_equals
215 (Telemetry_test_utils.int_exn telemetry
"decling.count")
216 "There should be this many decling_count for local_memory provider";
217 Asserter.Int_asserter.assert_equals
219 (Telemetry_test_utils.int_exn telemetry
"disk_cat.count")
220 "There should be 1 disk_cat_count for local_memory_provider");
224 let test_should_enable_deferring () =
225 Relative_path.set_path_prefix
227 (Path.make
@@ Common_setup.in_fake_dir
"www");
230 GlobalOptions.{ default
with tco_max_times_to_defer_type_checking
= Some
2 }
233 Typing_service_types.
236 Relative_path.create
Relative_path.Root
237 @@ Common_setup.in_fake_dir
"www/Foo.php";
241 Asserter.Bool_asserter.assert_equals
243 (Typing_check_service.should_enable_deferring
opts file)
244 "File should be deferred twice - below max";
246 let file = Typing_service_types.{ file with deferred_count = 2 } in
247 Asserter.Bool_asserter.assert_equals
249 (Typing_check_service.should_enable_deferring
opts file)
250 "File should not be deferred - at max";
252 let file = Typing_service_types.{ file with deferred_count = 3 } in
253 Asserter.Bool_asserter.assert_equals
255 (Typing_check_service.should_enable_deferring
opts file)
256 "File should not be deferred - above max";
259 GlobalOptions.{ default
with tco_max_times_to_defer_type_checking
= None
}
261 Asserter.Bool_asserter.assert_equals
263 (Typing_check_service.should_enable_deferring
opts file)
264 "File should be deferred - max is not set";
268 (* This test verifies quarantine. *)
269 let test_quarantine () =
270 Provider_backend.set_local_memory_backend_with_defaults
();
271 let { Common_setup.ctx; foo_path
; foo_contents
; nonexistent_path
; _
} =
272 Common_setup.setup ~sqlite
:false tcopt_with_defer
276 let (ctx, _foo_entry
) =
277 Provider_context.add_or_overwrite_entry_contents
280 ~contents
:foo_contents
284 Provider_utils.respect_but_quarantine_unsaved_changes ~
ctx ~f
:(fun () ->
286 with e
-> e
|> Exception.wrap
|> Exception.to_string
288 Asserter.String_asserter.assert_equals
291 "Should be able to quarantine foo";
293 (* repeat of simple case *)
296 Provider_utils.respect_but_quarantine_unsaved_changes ~
ctx ~f
:(fun () ->
298 with e
-> e
|> Exception.wrap
|> Exception.to_string
300 Asserter.String_asserter.assert_equals
303 "Should be able to quarantine foo a second time";
305 (* add a non-existent file; should fail *)
306 let (ctx2
, _nonexistent_entry
) =
307 Provider_context.add_or_overwrite_entry_contents
309 ~
path:nonexistent_path
314 Provider_utils.respect_but_quarantine_unsaved_changes
317 with e
-> e
|> Exception.wrap
|> Exception.to_string
319 Asserter.String_asserter.assert_equals
322 "Should be able to quarantine nonexistent_file";
324 (* repeat of simple case, back with original ctx *)
327 Provider_utils.respect_but_quarantine_unsaved_changes ~
ctx ~f
:(fun () ->
329 with e
-> e
|> Exception.wrap
|> Exception.to_string
331 Asserter.String_asserter.assert_equals
334 "Should be able to quarantine foo a third time";
340 ("test_deferred_decl_add", test_deferred_decl_add);
341 ("test_deferred_decl_should_defer", test_deferred_decl_should_defer);
342 ("test_process_file_deferring", test_process_file_deferring);
343 ("test_compute_tast_counting", test_compute_tast_counting);
344 ("test_dmesg_parser", test_dmesg_parser);
345 ("test_should_enable_deferring", test_should_enable_deferring);
346 ("test_quarantine", test_quarantine);
350 EventLogger.init_fake
();
351 (* The parsing service needs shared memory to be set up *)
356 heap_size
= 1024 * 8;
366 let (_
: SharedMem.handle
) = SharedMem.init
config ~num_workers
:0 in
368 |> List.map ~f
:(fun (name
, do_
) ->
372 ~enter
:Provider_backend.set_shared_memory_backend