|
1 | | -(** This test runs a battery of TLA+ syntax fragments against TLAPM's syntax |
2 | | - parser. In the future it will check the actual parse tree emitted by |
3 | | - TLAPM, but for now it just checks whether TLAPM parses without error all |
4 | | - the syntax it is expected to parse. Tests sourced from: |
| 1 | +(** This file runs a battery of TLA+ syntax fragments against TLAPM's syntax |
| 2 | + parser. It then takes the resulting parse tree and translates it into a |
| 3 | + normalized S-expression form to compare with the associated expected AST. |
| 4 | + Tests are sourced from the standardized syntax test corpus at: |
5 | 5 | https://github.com/tlaplus/rfcs/tree/2a772d9dd11acec5d7dedf30abfab91a49de48b8/language_standard/tests/tlaplus_syntax |
6 | 6 | *) |
7 | 7 |
|
| 8 | +open Tlapm_lib;; |
| 9 | + |
8 | 10 | open Syntax_corpus_file_parser;; |
9 | | -open OUnit2;; |
10 | 11 |
|
11 | 12 | (** Calls TLAPM's parser with the given input. Catches all exceptions and |
12 | 13 | treats them as parse failures. |
13 | 14 | @param input The TLA+ fragment to parse. |
14 | 15 | @return None if parse failure, syntax tree root if successful. |
15 | 16 | *) |
16 | | -let parse (input : string) : Tlapm_lib__M_t.mule option = |
17 | | - try Tlapm_lib.module_of_string input |
| 17 | +let parse (input : string) : Module.T.mule option = |
| 18 | + try module_of_string input |
18 | 19 | with _ -> None |
19 | 20 |
|
20 | | -(** Datatype summarizing a run of all the syntax tests. *) |
21 | | -type test_run_summary = { |
22 | | - total : int; |
23 | | - succeeded : int; |
24 | | - failed : int; |
25 | | - skipped : int; |
26 | | - failures : syntax_test_info list; |
27 | | -} [@@deriving show] |
28 | | - |
29 | | -(** A blank test summary. *) |
30 | | -let test_summary_init = { |
31 | | - total = 0; |
32 | | - succeeded = 0; |
33 | | - failed = 0; |
34 | | - skipped = 0; |
35 | | - failures = []; |
36 | | -} |
37 | | - |
38 | | -(** Runs a given syntax test by determining its type then sending the input |
39 | | - into the TLAPM parser. |
40 | | - @param expect_failure Whether this test should fail due to a TLAPM bug. |
41 | | - @param test Information about the test itself. |
42 | | - @return Whether the test succeeded. |
43 | | -*) |
44 | | -let run_test (test : syntax_test) : bool = |
45 | | - match test.test with |
46 | | - | Error_test input -> parse input |> Option.is_none |
47 | | - | Expected_test (input, _) -> parse input |> Option.is_some |
48 | | - |
49 | | -(** Controls run of a given syntax test. Checks whether test should be |
50 | | - skipped and whether it is expected to fail, then runs test and returns |
51 | | - summary. |
52 | | - @param expect_failure Whether this test should fail due to a TLAPM bug. |
53 | | - @param acc Accumulation variable for test summarization. |
54 | | - @param test Information about the test itself. |
55 | | - @return Test run summary. |
56 | | -*) |
57 | | -let control_test_run |
58 | | - (expect_failure : syntax_test -> bool) |
59 | | - (acc : test_run_summary) |
60 | | - (test : syntax_test) |
61 | | - : test_run_summary = |
62 | | - let acc = {acc with total = acc.total + 1} in |
63 | | - if test.skip then {acc with skipped = acc.skipped + 1} else |
64 | | - if run_test test = expect_failure test |
65 | | - then {acc with failed = acc.failed + 1; failures = test.info :: acc.failures} |
66 | | - else {acc with succeeded = acc.succeeded + 1} |
67 | | - |
68 | | -(** Given a path to a directory containing a corpus of syntax tests, get all |
69 | | - the tests encoded in those files, filter them as appropriate, then run |
70 | | - them all and collect the results. |
71 | | - @param path Path to the directory containing the corpus of syntax tests. |
72 | | - @param expect_failure Whether a test should fail due to a TLAPM bug. |
73 | | - @param filter_predicate Whether to actually execute a test. |
74 | | - @return Accumulated summary of all test executions. |
75 | | -*) |
76 | | -let run_test_corpus |
77 | | - (path : string) |
78 | | - (expect_failure : syntax_test -> bool) |
79 | | - (filter_pred : syntax_test -> bool) |
80 | | - : test_run_summary = |
81 | | - path |
82 | | - |> get_all_tests_under |
83 | | - |> List.filter filter_pred |
84 | | - |> List.fold_left (control_test_run expect_failure) test_summary_init |
85 | | - |
86 | 21 | (** Names of tests that are known to fail due to TLAPM parser bugs. |
87 | 22 | @param test Information about the test. |
88 | 23 | @return Whether the test is expected to fail. |
89 | 24 | *) |
90 | | -let expect_failure (test : syntax_test) : bool = |
| 25 | +let expect_parse_failure (test : syntax_test) : bool = |
91 | 26 | List.mem test.info.name [ |
92 | 27 |
|
93 | 28 | (* https://github.com/tlaplus/tlapm/issues/54#issuecomment-2435515180 *) |
@@ -142,24 +77,97 @@ let expect_failure (test : syntax_test) : bool = |
142 | 77 | "Nonfix Double Exclamation Operator (GH TSTLA #GH97, GH tlaplus/tlaplus #884)"; |
143 | 78 | ] |
144 | 79 |
|
145 | | -(** Filter predicate to control which tests to run. |
146 | | - @param name Optional; a test name to filter on. |
147 | | - @return Predicate matching all tests or tests with given name. |
| 80 | +(** Names of tests that are unable to match the expected output tree, but not |
| 81 | + because of a bug; instead, the TLAPM syntax tree doesn't contain the |
| 82 | + (usually extraneous) necessary information to fully populate the output |
| 83 | + tree with the expected children. |
| 84 | + @param test Information about the test. |
| 85 | + @return Whether the test should skip the tree comparison phase. |
| 86 | +*) |
| 87 | +let should_skip_tree_comparison (test : syntax_test) : bool = |
| 88 | + List.mem test.info.name [ |
| 89 | + (* In TLAPM's ASSUME/PROVE parsing, NEW identifiers with unspecified |
| 90 | + level are by default Constant instead of Unknown *) |
| 91 | + "Assume/Prove With New Identifier of Unspecified Level"; |
| 92 | + |
| 93 | + (* Jlist terminated by single line comment omitted in TLAPM AST *) |
| 94 | + "Keyword-Unit-Terminated Conjlist"; |
| 95 | + "Keyword-Unit-Terminated Disjlist"; |
| 96 | + |
| 97 | + (* Unnecessary parentheses omitted in TLAPM AST *) |
| 98 | + "Nested Parentheses"; |
| 99 | + |
| 100 | + (* TLAPM AST does not distinguish between nonfix and infix ops *) |
| 101 | + "Lexically-Conflicting Nonfix Operators"; |
| 102 | + "Minus and Negative"; |
| 103 | + "Nonfix Minus (GH tlaplus/tlaplus #GH884)"; |
| 104 | + "Nonfix Prefix Operators"; |
| 105 | + "Nonfix Infix Operators"; |
| 106 | + "Nonfix Postfix Operators"; |
| 107 | + |
| 108 | + (* TLAPM uses function literals for function definitions *) |
| 109 | + (* See: https://github.com/tlaplus/tlapm/issues/237 *) |
| 110 | + "Function Literal"; |
| 111 | + |
| 112 | + (* TLAPM makes multi-parameter EXCEPT update statements into tuples *) |
| 113 | + "Record Update with Multiple Parameters"; |
| 114 | + "Record Update with Tuple and Non-Tuple Parameters"; |
| 115 | + |
| 116 | + (* TLAPM does not distinguish between <=> and \equiv *) |
| 117 | + "IFF Disambiguation" |
| 118 | + ] |
| 119 | + |
| 120 | +(** Names of tests that are expected to fail the tree comparison phase due to |
| 121 | + bugs in TLAPM's syntax parser. |
| 122 | + @param test Information about the test. |
| 123 | + @return Whether the test is expected to fail the tree comparison phase. |
148 | 124 | *) |
149 | | -let should_run ?name test = |
150 | | - match name with |
151 | | - | Some name -> String.equal test.info.name name |
152 | | - | None -> true |
| 125 | +let expect_tree_comparison_failure (test : syntax_test) : bool = |
| 126 | + List.mem test.info.name [ |
| 127 | + (* TLAPM appears to simply return an empty set here? *) |
| 128 | + (* https://github.com/tlaplus/tlapm/issues/235 *) |
| 129 | + "Mistaken Set Filter Test"; |
| 130 | + "Mistaken Set Filter Tuples Test"; |
| 131 | + ] |
| 132 | + |
| 133 | +open OUnit2;; |
153 | 134 |
|
154 | | -(** The top-level test; runs all syntax tests, prints summary, then fails |
155 | | - with an assertion if any tests failed. |
| 135 | +(** Gathers all syntax test files, parses them, then runs the cases they |
| 136 | + contain as tests against TLAPM's syntax parser, skipping or expecting |
| 137 | + failure as appropriate. |
156 | 138 | *) |
157 | | -let () = |
158 | | - let test_results = |
159 | | - run_test_corpus |
160 | | - "syntax_corpus" |
161 | | - expect_failure |
162 | | - (should_run (*~name:"Proof Containing Jlist"*)) |
163 | | - in |
164 | | - print_endline (show_test_run_summary test_results); |
165 | | - assert_equal 0 test_results.failed; |
| 139 | +let tests = "Standardized syntax test corpus" >::: ( |
| 140 | + get_all_tests_under "syntax_corpus" |
| 141 | + |> List.map (fun test -> |
| 142 | + Format.sprintf "[%s] %s" test.info.path test.info.name >:: |
| 143 | + (fun _ -> |
| 144 | + skip_if test.skip "Test has skip attribute"; |
| 145 | + match test.test with |
| 146 | + | Error_test input -> ( |
| 147 | + match parse input with |
| 148 | + | None -> assert_bool "Expected error test to fail" (not (expect_parse_failure test)) |
| 149 | + | Some _ -> assert_bool "Expected parse failure" (expect_parse_failure test) |
| 150 | + ) |
| 151 | + | Expected_test (input, expected) -> ( |
| 152 | + match parse input with |
| 153 | + | None -> assert_bool "Expected parse success" (expect_parse_failure test) |
| 154 | + | Some tlapm_output -> |
| 155 | + skip_if (should_skip_tree_comparison test) "Skipping parse tree comparison"; |
| 156 | + let open Translate_syntax_tree in |
| 157 | + let open Sexplib in |
| 158 | + let actual = tlapm_output |> translate_tla_source_file |> ts_node_to_sexpr in |
| 159 | + if Sexp.equal expected actual |
| 160 | + then assert_bool "Expected parse test to fail" (not (expect_tree_comparison_failure test)) |
| 161 | + else |
| 162 | + let open Sexp_diff in |
| 163 | + let diff = Algo.diff ~original:expected ~updated:actual () in |
| 164 | + let options = Display.Display_options.(create Layout.Single_column) in |
| 165 | + let text = Display.display_with_ansi_colors options diff in |
| 166 | + assert_bool text (expect_tree_comparison_failure test) |
| 167 | + ) |
| 168 | + ) |
| 169 | + ) |
| 170 | +) |
| 171 | + |
| 172 | +(** The OUnit2 test entrypoint. *) |
| 173 | +let () = run_test_tt_main tests |
0 commit comments