2 * Tests documenting various invariants about the order of things coming out
3 * of Errors module. Some of them are __probably__ not necessary for
4 * correctness, but if you break them you will still change a bunch of tests
5 * outputs and will have to spend time wondering whether the changes are
11 let error_list_to_string_buffer buf x
=
12 List.iter x ~f
:(fun error
->
13 Printf.bprintf buf
"%s\n" Errors.(error
|> to_absolute
|> to_string
))
15 let error_list_to_string errors
=
16 let buf = Buffer.create
1024 in
17 error_list_to_string_buffer buf errors
;
20 let create_path x
= Relative_path.(create Root
("/" ^ x
))
22 let error_in file
= Errors.parsing_error
(Pos.make_from
(create_path file
), "")
25 Printf.sprintf
"File \"/%s\", line 0, characters 0-0:\n (Parsing[1002])\n\n"
34 let expected = expect_error_in "A" ^
expect_error_in "B" in
35 Asserter.String_asserter.assert_equals
37 (Errors.get_error_list errors
|> error_list_to_string)
38 "Errors should be returned from do_ in the order they were added";
39 Asserter.String_asserter.assert_equals
41 (Errors.get_sorted_error_list errors
|> error_list_to_string)
42 "get_sorted_error_list should sort errors by filename";
45 let expected_unsorted =
46 {|File
"/FileWithErrors.php", line
1, characters
4-7:
47 This
value is not a valid key
type for this container
(Typing
[4298])
48 File
"/C2", line
0, characters
0-0:
49 This container is C2_Type
50 File
"/K2", line
0, characters
0-0:
51 K2_Type cannot be used
as a key
for C2_Type
53 File
"/FileWithErrors.php", line
1, characters
4-7:
54 This
value is not a valid key
type for this container
(Typing
[4298])
55 File
"/C1", line
0, characters
0-0:
56 This container is C1_Type
57 File
"/K1", line
0, characters
0-0:
58 K1_Type cannot be used
as a key
for C1_Type
60 File
"/FileWithErrors.php", line
0, characters
0-0:
63 File
"/FileWithErrors.php", line
1, characters
4-7:
64 This
value is not a valid key
type for this container
(Typing
[4298])
65 File
"/C2", line
0, characters
0-0:
66 This container is C2_Type
67 File
"/K2", line
0, characters
0-0:
68 K2_Type cannot be used
as a key
for C2_Type
70 File
"/FileWithErrors.php", line
1, characters
4-7:
71 This
value is not a valid key
type for this container
(Typing
[4298])
72 File
"/C1", line
0, characters
0-0:
73 This container is C1_Type
74 File
"/K1", line
0, characters
0-0:
75 K1_Type cannot be used
as a key
for C1_Type
80 {|File
"/FileWithErrors.php", line
0, characters
0-0:
83 File
"/FileWithErrors.php", line
1, characters
4-7:
84 This
value is not a valid key
type for this container
(Typing
[4298])
85 File
"/C1", line
0, characters
0-0:
86 This container is C1_Type
87 File
"/K1", line
0, characters
0-0:
88 K1_Type cannot be used
as a key
for C1_Type
90 File
"/FileWithErrors.php", line
1, characters
4-7:
91 This
value is not a valid key
type for this container
(Typing
[4298])
92 File
"/C2", line
0, characters
0-0:
93 This container is C2_Type
94 File
"/K2", line
0, characters
0-0:
95 K2_Type cannot be used
as a key
for C2_Type
99 let test_get_sorted_error_list () =
101 Errors.do_
(fun () ->
106 let expected = expect_error_in "B" ^
expect_error_in "A" in
107 Asserter.String_asserter.assert_equals
109 (Errors.get_error_list errors
|> error_list_to_string)
110 "Errors should be returned from do_ in the order they were added";
112 let expected = expect_error_in "A" ^
expect_error_in "B" in
113 Asserter.String_asserter.assert_equals
115 (Errors.get_sorted_error_list errors
|> error_list_to_string)
116 "get_sorted_error_list should sort errors by filename";
118 let file_with_errors = create_path "FileWithErrors.php" in
120 Pos.make_from_lnum_bol_cnum
121 ~pos_file
:file_with_errors
125 Printf.printf
"%s" (Pos.print_verbose_relative
err_pos);
126 let container_pos1 = Pos.make_from
(create_path "C1") in
127 let container_pos2 = Pos.make_from
(create_path "C2") in
128 let key_pos1 = Pos.make_from
(create_path "K1") in
129 let key_pos2 = Pos.make_from
(create_path "K2") in
131 Errors.do_with_context
file_with_errors Errors.Typing
(fun () ->
132 Errors.invalid_arraykey_read
134 (container_pos2, "C2_Type")
135 (key_pos2, "K2_Type");
136 Errors.invalid_arraykey_read
138 (container_pos1, "C1_Type")
139 (key_pos1, "K1_Type");
140 error_in "FileWithErrors.php";
141 Errors.invalid_arraykey_read
143 (container_pos2, "C2_Type")
144 (key_pos2, "K2_Type");
145 Errors.invalid_arraykey_read
147 (container_pos1, "C1_Type")
148 (key_pos1, "K1_Type");
151 Asserter.String_asserter.assert_equals
153 (Errors.get_error_list errors
|> error_list_to_string)
154 "Errors should be returned in the order they were added";
156 Asserter.String_asserter.assert_equals
158 (Errors.get_sorted_error_list errors
|> error_list_to_string)
159 "get_sorted_error_list should sort errors by position, code, and warrant";
164 let error_ref = ref None
in
172 error_ref := Some error
;
175 let expected = expect_error_in "A" in
176 match !error_ref with
177 | None
-> failwith
"Expected error handler to run"
179 Asserter.String_asserter.assert_equals
181 (error_list_to_string [e
])
182 "Errors.try_ should call the handler with first encountered error";
187 Errors.do_
(fun () ->
193 Errors.do_
(fun () ->
198 let errors = Errors.merge errors1 errors2
in
201 ^
expect_error_in "A"
202 ^
expect_error_in "C"
203 ^
expect_error_in "D"
205 Asserter.String_asserter.assert_equals
207 (Errors.get_error_list
errors |> error_list_to_string)
208 "Errors.merge behaves like List.rev_append";
210 let errors = Errors.merge errors1
Errors.empty
in
211 let expected = expect_error_in "B" ^
expect_error_in "A" in
212 Asserter.String_asserter.assert_equals
214 (Errors.get_error_list
errors |> error_list_to_string)
215 "Errors.merge behaves like List.rev_append";
217 let errors = Errors.merge
Errors.empty errors2
in
218 let expected = expect_error_in "C" ^
expect_error_in "D" in
219 Asserter.String_asserter.assert_equals
221 (Errors.get_error_list
errors |> error_list_to_string)
222 "Errors.merge behaves like List.rev_append";
225 let test_from_error_list () =
227 Errors.do_
(fun () ->
232 let errors = Errors.get_error_list
errors in
233 let expected = error_list_to_string errors in
234 let errors = Errors.(errors |> from_error_list
|> get_error_list
) in
235 Asserter.String_asserter.assert_equals
237 (errors |> error_list_to_string)
238 "get_error_list(from_error_list(x)) == x";
239 Asserter.Bool_asserter.assert_equals
241 Errors.([] |> from_error_list
|> is_empty
)
242 "is_empty(from_error_list([])) == true";
246 let a_path = create_path "A" in
248 Errors.do_
(fun () ->
249 Errors.run_in_context
a_path Errors.Parsing
(fun () ->
250 Errors.parsing_error
(Pos.make_from
a_path, ""));
251 Errors.run_in_context
a_path Errors.Typing
(fun () ->
252 Errors.typing_error
(Pos.make_from
a_path) "");
256 "File \"/A\", line 0, characters 0-0:\n (Parsing[1002])\n\n"
257 ^
"File \"/A\", line 0, characters 0-0:\n (Typing[4116])\n\n"
259 Asserter.String_asserter.assert_equals
261 (Errors.get_error_list
errors |> error_list_to_string)
262 "Errors from earlier phase should come first";
265 let test_incremental_update () =
266 let a_path = create_path "A" in
267 let b_path = create_path "B" in
268 let (foo_error_a
, ()) =
269 Errors.do_with_context
a_path Errors.Parsing
(fun () ->
274 let (bar_error_a
, ()) =
275 Errors.do_with_context
a_path Errors.Parsing
(fun () ->
280 let (baz_error_b
, ()) =
281 Errors.do_with_context
b_path Errors.Parsing
(fun () ->
287 Errors.incremental_update_set
290 ~rechecked
:(Relative_path.Set.singleton
a_path)
294 "File \"/bar2\", line 0, characters 0-0:\n (Parsing[1002])\n\n"
295 ^
"File \"/bar1\", line 0, characters 0-0:\n (Parsing[1002])\n\n"
297 Asserter.String_asserter.assert_equals
299 (Errors.get_error_list
errors |> error_list_to_string)
300 "Incremental update should overwrite foo error with bar.";
303 Errors.incremental_update_set
306 ~rechecked
:(Relative_path.Set.singleton
b_path)
310 "File \"/foo1\", line 0, characters 0-0:\n (Parsing[1002])\n\n"
311 ^
"File \"/foo2\", line 0, characters 0-0:\n (Parsing[1002])\n\n"
312 ^
"File \"/baz2\", line 0, characters 0-0:\n (Parsing[1002])\n\n"
313 ^
"File \"/baz1\", line 0, characters 0-0:\n (Parsing[1002])\n\n"
315 Asserter.String_asserter.assert_equals
317 (Errors.get_error_list
errors |> error_list_to_string)
318 "Incremental update should add baz error and leave foo error unchanged";
321 Errors.incremental_update_set
324 ~rechecked
:(Relative_path.Set.singleton
a_path)
327 Asserter.Bool_asserter.assert_equals
329 (Errors.is_empty
errors)
330 "Incremental update should clear errors if a rechecked file has no errors";
333 let test_merge_into_current () =
335 Errors.do_
(fun () ->
346 ^
expect_error_in "B"
347 ^
expect_error_in "C"
348 ^
expect_error_in "D"
349 ^
expect_error_in "E"
350 ^
expect_error_in "F"
353 "merge_into_current should behave as if the code that "
354 ^
"generated errors was inlined at the callsite."
356 Asserter.String_asserter.assert_equals
358 (Errors.get_error_list errors1
|> error_list_to_string)
361 let (sub_errors
, ()) =
362 Errors.do_
(fun () ->
368 Errors.do_
(fun () ->
369 Errors.merge_into_current sub_errors
;
372 let expected2 = expect_error_in "C" ^
expect_error_in "D" in
373 Asserter.String_asserter.assert_equals
375 (Errors.get_error_list errors2
|> error_list_to_string)
379 Errors.do_
(fun () ->
382 Errors.merge_into_current sub_errors
;
387 Asserter.String_asserter.assert_equals
389 (Errors.get_error_list
errors |> error_list_to_string)
393 (* Errors.merge is called on very critical paths in Parsing_service,
394 * Decl_redecl_service, and Typing_check_service to merge partial results from
395 * workers. If it's too slow, it delays scheduling of more jobs, and hurts
396 * parallelism rate. All those callsites pass the second argument as the
397 * accumulator, so the runtime needs to be proportional to the size of first
399 let test_performance () =
401 let rec aux acc
= function
404 let path = string_of_int
n ^
".php" in
406 Errors.(do_with_context
(create_path path) Typing
) (fun () ->
409 (* note argument order: small first, big second *)
410 aux (Errors.merge
errors acc
) (n - 1)
412 let errors = aux Errors.empty
n in
413 List.length
(Errors.get_error_list
errors) == n
418 ("test_get_sorted_error_list", test_get_sorted_error_list);
419 ("test_try", test_try);
420 ("test_merge", test_merge);
421 ("test_from_error_list", test_from_error_list);
422 ("test_phases", test_phases);
423 (* TODO T44055462 please amend test to maintain new invariants of error API
424 "test_incremental_update", test_incremental_update; *)
425 ("test_merge_into_current", test_merge_into_current);
426 ("test_performance", test_performance);
430 Relative_path.(set_path_prefix Root
(Path.make
"/"));
431 Unit_test.run_all
tests