expose defered_decl count
[hiphop-php.git] / hphp / hack / test / unit / server_tests.ml
blobf433530425eba94797a0b16f2ae27007847d2f87
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 Core_kernel
13 let test_process_data =
14 ServerProcess.
16 pid = 2758734;
17 finale_file = "2758734.fin";
18 start_t = 0.0;
19 in_fd = Unix.stdin;
20 out_fds = [("default", Unix.stdout)];
21 last_request_handoff = ref 0.0;
24 let test_dmesg_parser () =
25 let input =
27 "[3034339.262439] Out of memory: Kill process 2758734 (hh_server) score 253 or sacrifice child";
30 Sys_utils.find_oom_in_dmesg_output
31 test_process_data.ServerProcess.pid
32 "hh_server"
33 input
35 let ensure_count (count : int) : unit =
36 let deferred = Deferred_decl.get_deferments ~f:(fun d -> d) in
37 Asserter.Int_asserter.assert_equals
38 count
39 (List.length deferred)
40 "The number of deferred items should match the expected value"
42 let test_deferred_decl_add () =
43 Deferred_decl.reset ~enable:true;
44 ensure_count 0;
46 Deferred_decl.add (Relative_path.create Relative_path.Dummy "foo");
47 ensure_count 1;
49 Deferred_decl.add (Relative_path.create Relative_path.Dummy "foo");
50 ensure_count 1;
52 Deferred_decl.add (Relative_path.create Relative_path.Dummy "bar");
53 ensure_count 2;
55 Deferred_decl.reset ~enable:true;
56 ensure_count 0;
58 true
60 let ensure_threshold ~(threshold : int) ~(limit : int) ~(expected : int) : unit
62 Deferred_decl.reset ~enable:true;
63 ensure_count 0;
65 let deferred_count = ref 0 in
66 for i = 1 to limit do
67 let path = Printf.sprintf "foo-%d" i in
68 let relative_path = Relative_path.create Relative_path.Dummy path in
69 try
70 Deferred_decl.should_defer
71 ~d:relative_path
72 ~threshold_opt:(Some threshold)
73 with Deferred_decl.Defer d ->
74 Asserter.Bool_asserter.assert_equals
75 (i >= threshold)
76 true
77 (Printf.sprintf
78 "We should have reached the threshold %d, i=%d"
79 threshold
80 i);
81 Asserter.String_asserter.assert_equals
82 (Relative_path.suffix d)
83 path
84 "The deferred path should be the last one we saw";
85 deferred_count := !deferred_count + 1
86 done;
88 Asserter.Int_asserter.assert_equals
89 expected
90 !deferred_count
91 (Printf.sprintf
92 "Unexpected deferred count; threshold: %d; limit: %d; expected: %d"
93 threshold
94 limit
95 expected)
97 let test_deferred_decl_should_defer () =
98 ensure_threshold ~threshold:0 ~limit:1 ~expected:1;
99 ensure_threshold ~threshold:1 ~limit:2 ~expected:1;
100 ensure_threshold ~threshold:2 ~limit:1 ~expected:0;
101 ensure_threshold ~threshold:1 ~limit:5 ~expected:4;
103 true
105 let foo_contents =
106 {|<?hh //strict
107 class Foo {
108 public function foo (Bar $b): int {
109 return $b->toString();
114 let bar_contents =
115 {|<?hh //strict
116 class Bar {
117 public function toString() : string {
118 return "bar";
123 (* In this test, we wish to establish that we enable deferring type checking
124 for files that have undeclared dependencies, UNLESS we've already deferred
125 those files a certain number of times. *)
126 let test_process_file_deferring () =
127 Deferred_decl.reset ~enable:true;
129 (* Set up a simple fake repo *)
130 Disk.mkdir_p "/fake/root/";
131 Relative_path.set_path_prefix Relative_path.Root (Path.make "/fake/root/");
133 (* We'll need to parse these files in order to create a naming table, which
134 will be used for look up of symbols in type checking. *)
135 Disk.write_file ~file:"/fake/root/Foo.php" ~contents:foo_contents;
136 Disk.write_file ~file:"/fake/root/Bar.php" ~contents:bar_contents;
137 let foo_path = Relative_path.create Relative_path.Root "/fake/root/Foo.php" in
138 let bar_path = Relative_path.create Relative_path.Root "/fake/root/Bar.php" in
139 (* The parsing service needs shared memory to be set up *)
140 let config =
141 SharedMem.
143 global_size = 1024;
144 heap_size = 1024 * 8;
145 dep_table_pow = 16;
146 hash_table_pow = 10;
147 shm_dirs = [];
148 shm_min_avail = 0;
149 log_level = 0;
150 sample_rate = 0.0;
153 let (_ : SharedMem.handle) = SharedMem.init config ~num_workers:0 in
154 (* Parsing produces the file infos that the naming table module can use
155 to construct the forward naming table (files-to-symbols) *)
156 let (file_infos, _errors, _failed_parsing) =
157 Parsing_service.go
158 None
159 Relative_path.Set.empty
160 ~get_next:(MultiWorker.next None [foo_path; bar_path])
161 ParserOptions.default
162 ~trace:true
164 let naming_table = Naming_table.create file_infos in
165 (* Construct the reverse naming table (symbols-to-files) *)
166 let fast = Naming_table.to_fast naming_table in
167 Relative_path.Map.iter fast ~f:(fun name info ->
168 let {
169 FileInfo.n_classes = classes;
170 n_record_defs = record_defs;
171 n_types = typedefs;
172 n_funs = funs;
173 n_consts = consts;
175 info
177 Naming_global.ndecl_file_fast
178 name
179 ~funs
180 ~classes
181 ~record_defs
182 ~typedefs
183 ~consts);
185 (* Construct one instance of file type check computation. Class \Foo depends
186 on class \Bar being declared, so processing \Foo once should result in
187 two computations:
188 - a declaration of \Bar
189 - a (deferred) type check of \Foo *)
190 let dynamic_view_files = Relative_path.Set.empty in
191 let opts =
192 Global_naming_options.set
193 GlobalOptions.
194 { default with tco_defer_class_declaration_threshold = Some 1 };
195 Global_naming_options.get ()
197 let errors = Errors.empty in
198 let file = Typing_check_service.{ path = foo_path; deferred_count = 0 } in
199 (* Finally, this is what all the setup was for: process this file *)
200 let (_errors, file_computations) =
201 Typing_check_service.process_file dynamic_view_files opts errors file
203 Asserter.Int_asserter.assert_equals
205 (List.length file_computations)
206 "Should be two file computations";
208 (* Validate the deferred type check computation *)
209 let found_check =
210 List.exists file_computations ~f:(fun file_computation ->
211 match file_computation with
212 | Typing_check_service.(Check { path; deferred_count }) ->
213 Asserter.String_asserter.assert_equals
214 "Foo.php"
215 (Relative_path.suffix path)
216 "Check path must match the expected";
217 Asserter.Int_asserter.assert_equals
219 deferred_count
220 "Check deferred count must match the expected";
221 true
222 | _ -> false)
224 Asserter.Bool_asserter.assert_equals
225 true
226 found_check
227 "Should have found the check file computation";
229 (* Validate the declare file computation *)
230 let found_declare =
231 List.exists file_computations ~f:(fun file_computation ->
232 match file_computation with
233 | Typing_check_service.Declare path ->
234 Asserter.String_asserter.assert_equals
235 "Bar.php"
236 (Relative_path.suffix path)
237 "Declare path must match the expected";
238 true
239 | _ -> false)
241 Asserter.Bool_asserter.assert_equals
242 true
243 found_declare
244 "Should have found the declare file computation";
246 true
248 let test_should_enable_deferring () =
249 Relative_path.set_path_prefix Relative_path.Root (Path.make "/fake/www");
251 let opts =
252 GlobalOptions.{ default with tco_max_times_to_defer_type_checking = Some 2 }
254 let file =
255 Typing_check_service.
257 path = Relative_path.create Relative_path.Root "/fake/www/Foo.php";
258 deferred_count = 1;
261 Asserter.Bool_asserter.assert_equals
262 true
263 (Typing_check_service.should_enable_deferring opts file)
264 "File should be deferred twice - below max";
266 let file = Typing_check_service.{ file with deferred_count = 2 } in
267 Asserter.Bool_asserter.assert_equals
268 false
269 (Typing_check_service.should_enable_deferring opts file)
270 "File should not be deferred - at max";
272 let file = Typing_check_service.{ file with deferred_count = 3 } in
273 Asserter.Bool_asserter.assert_equals
274 false
275 (Typing_check_service.should_enable_deferring opts file)
276 "File should not be deferred - above max";
278 let opts =
279 GlobalOptions.{ default with tco_max_times_to_defer_type_checking = None }
281 Asserter.Bool_asserter.assert_equals
282 true
283 (Typing_check_service.should_enable_deferring opts file)
284 "File should be deferred - max is not set";
286 true
288 let tests =
290 ("test_deferred_decl_add", test_deferred_decl_add);
291 ("test_deferred_decl_should_defer", test_deferred_decl_should_defer);
292 ("test_process_file_deferring", test_process_file_deferring);
293 ("test_dmesg_parser", test_dmesg_parser);
294 ("test_should_enable_deferring", test_should_enable_deferring);
297 let () = Unit_test.run_all tests