server_specific_files
[hiphop-php.git] / hphp / hack / test / unit / server_tests.ml
blob6951dec561969797e5379270e6dc262f36005d6a
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 open Hh_prelude
13 let tcopt_with_defer =
14 GlobalOptions.{ default with tco_defer_class_declaration_threshold = Some 1 }
16 let test_process_data =
17 ServerProcess.
19 pid = 2758734;
20 server_specific_files =
21 { ServerCommandTypes.server_finale_file = "2758734.fin" };
22 start_t = 0.0;
23 in_fd = Unix.stdin;
24 out_fds = [("default", Unix.stdout)];
25 last_request_handoff = ref 0.0;
28 let test_dmesg_parser () =
29 let input =
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
36 "hh_server"
37 input
39 let ensure_count (count : int) : unit =
40 let deferred = Deferred_decl.get_deferments () in
41 Asserter.Int_asserter.assert_equals
42 count
43 (List.length deferred)
44 "The number of deferred items should match the expected value"
46 let test_deferred_decl_add () =
47 Deferred_decl.reset
48 ~enable:true
49 ~declaration_threshold_opt:None
50 ~memory_mb_threshold_opt:None;
51 ensure_count 0;
53 Deferred_decl.add_deferment
54 ~d:(Relative_path.create Relative_path.Dummy "foo", "\\Foo");
55 ensure_count 1;
57 Deferred_decl.add_deferment
58 ~d:(Relative_path.create Relative_path.Dummy "foo", "\\Foo");
59 ensure_count 1;
61 Deferred_decl.add_deferment
62 ~d:(Relative_path.create Relative_path.Dummy "bar", "\\Bar");
63 ensure_count 2;
65 Deferred_decl.reset
66 ~enable:true
67 ~declaration_threshold_opt:None
68 ~memory_mb_threshold_opt:None;
69 ensure_count 0;
71 true
73 let ensure_threshold ~(threshold : int) ~(limit : int) ~(expected : int) : unit
75 Deferred_decl.reset
76 ~enable:true
77 ~declaration_threshold_opt:(Some threshold)
78 ~memory_mb_threshold_opt:None;
79 ensure_count 0;
81 let deferred_count = ref 0 in
82 for i = 1 to limit do
83 let path = Printf.sprintf "foo-%d" i in
84 let relative_path = Relative_path.create Relative_path.Dummy path in
85 try
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
90 (i >= threshold)
91 true
92 (Printf.sprintf
93 "We should have reached the threshold %d, i=%d"
94 threshold
95 i);
96 Asserter.String_asserter.assert_equals
97 (Relative_path.suffix d)
98 path
99 "The deferred path should be the last one we saw";
100 deferred_count := !deferred_count + 1
101 done;
103 Asserter.Int_asserter.assert_equals
104 expected
105 !deferred_count
106 (Printf.sprintf
107 "Unexpected deferred count; threshold: %d; limit: %d; expected: %d"
108 threshold
109 limit
110 expected)
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;
118 true
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
146 true
147 (Telemetry_test_utils.int_exn counters "decling.count" > 0)
148 "Should be at least one decl fetched";
150 (* Validate the declare file computation *)
151 let found_declare =
152 List.exists deferred_decls ~f:(fun (deferred_file, _) ->
153 Asserter.String_asserter.assert_equals
154 "Bar.php"
155 (Relative_path.suffix deferred_file)
156 "Declare path must match the expected";
157 true)
159 Asserter.Bool_asserter.assert_equals
160 true
161 found_declare
162 "Should have found the declare file computation";
164 true
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
173 let (ctx, entry) =
174 Provider_context.add_or_overwrite_entry_contents
175 ~ctx
176 ~path:foo_path
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 *)
193 Utils.with_context
194 ~enter:(fun () ->
195 Provider_backend.set_local_memory_backend_with_defaults ())
196 ~exit:(fun () ->
197 (* restore it back to shared_mem for the rest of the tests *)
198 Provider_backend.set_shared_memory_backend ())
199 ~do_:(fun () ->
200 let ctx =
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
207 let (ctx, entry) =
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");
222 true
224 let test_should_enable_deferring () =
225 Relative_path.set_path_prefix
226 Relative_path.Root
227 (Path.make @@ Common_setup.in_fake_dir "www");
229 let opts =
230 GlobalOptions.{ default with tco_max_times_to_defer_type_checking = Some 2 }
232 let file =
233 Typing_service_types.
235 path =
236 Relative_path.create Relative_path.Root
237 @@ Common_setup.in_fake_dir "www/Foo.php";
238 deferred_count = 1;
241 Asserter.Bool_asserter.assert_equals
242 true
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
248 false
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
254 false
255 (Typing_check_service.should_enable_deferring opts file)
256 "File should not be deferred - above max";
258 let opts =
259 GlobalOptions.{ default with tco_max_times_to_defer_type_checking = None }
261 Asserter.Bool_asserter.assert_equals
262 true
263 (Typing_check_service.should_enable_deferring opts file)
264 "File should be deferred - max is not set";
266 true
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
275 (* simple case *)
276 let (ctx, _foo_entry) =
277 Provider_context.add_or_overwrite_entry_contents
278 ~ctx
279 ~path:foo_path
280 ~contents:foo_contents
282 let can_quarantine =
284 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
285 "ok")
286 with e -> e |> Exception.wrap |> Exception.to_string
288 Asserter.String_asserter.assert_equals
289 "ok"
290 can_quarantine
291 "Should be able to quarantine foo";
293 (* repeat of simple case *)
294 let can_quarantine =
296 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
297 "ok")
298 with e -> e |> Exception.wrap |> Exception.to_string
300 Asserter.String_asserter.assert_equals
301 "ok"
302 can_quarantine
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
308 ~ctx
309 ~path:nonexistent_path
310 ~contents:""
312 let can_quarantine =
314 Provider_utils.respect_but_quarantine_unsaved_changes
315 ~ctx:ctx2
316 ~f:(fun () -> "ok")
317 with e -> e |> Exception.wrap |> Exception.to_string
319 Asserter.String_asserter.assert_equals
320 "ok"
321 can_quarantine
322 "Should be able to quarantine nonexistent_file";
324 (* repeat of simple case, back with original ctx *)
325 let can_quarantine =
327 Provider_utils.respect_but_quarantine_unsaved_changes ~ctx ~f:(fun () ->
328 "ok")
329 with e -> e |> Exception.wrap |> Exception.to_string
331 Asserter.String_asserter.assert_equals
332 "ok"
333 can_quarantine
334 "Should be able to quarantine foo a third time";
336 true
338 let tests =
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);
349 let () =
350 EventLogger.init_fake ();
351 (* The parsing service needs shared memory to be set up *)
352 let config =
353 SharedMem.
355 global_size = 1024;
356 heap_size = 1024 * 8;
357 dep_table_pow = 16;
358 hash_table_pow = 10;
359 shm_dirs = [];
360 shm_min_avail = 0;
361 log_level = 0;
362 sample_rate = 0.0;
363 compression = 0;
366 let (_ : SharedMem.handle) = SharedMem.init config ~num_workers:0 in
367 tests
368 |> List.map ~f:(fun (name, do_) ->
369 ( name,
370 fun () ->
371 Utils.with_context
372 ~enter:Provider_backend.set_shared_memory_backend
373 ~exit:(fun () -> ())
374 ~do_ ))
375 |> Unit_test.run_all