xref: /openbsd-src/gnu/llvm/llvm/utils/lit/tests/unit/TestRunner.py (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
1# RUN: %{python} %s
2#
3# END.
4
5
6import os.path
7import platform
8import unittest
9
10import lit.discovery
11import lit.LitConfig
12import lit.Test as Test
13from lit.TestRunner import ParserKind, IntegratedTestKeywordParser, \
14                           parseIntegratedTestScript
15
16
17class TestIntegratedTestKeywordParser(unittest.TestCase):
18    inputTestCase = None
19
20    @staticmethod
21    def load_keyword_parser_lit_tests():
22        """
23        Create and load the LIT test suite and test objects used by
24        TestIntegratedTestKeywordParser
25        """
26        # Create the global config object.
27        lit_config = lit.LitConfig.LitConfig(progname='lit',
28                                             path=[],
29                                             quiet=False,
30                                             useValgrind=False,
31                                             valgrindLeakCheck=False,
32                                             valgrindArgs=[],
33                                             noExecute=False,
34                                             debug=False,
35                                             isWindows=(
36                                               platform.system() == 'Windows'),
37                                             order='smart',
38                                             params={})
39        TestIntegratedTestKeywordParser.litConfig = lit_config
40        # Perform test discovery.
41        test_path = os.path.dirname(os.path.dirname(__file__))
42        inputs = [os.path.join(test_path, 'Inputs/testrunner-custom-parsers/')]
43        assert os.path.isdir(inputs[0])
44        tests = lit.discovery.find_tests_for_inputs(lit_config, inputs, False)
45        assert len(tests) == 1 and "there should only be one test"
46        TestIntegratedTestKeywordParser.inputTestCase = tests[0]
47
48    @staticmethod
49    def make_parsers():
50        def custom_parse(line_number, line, output):
51            if output is None:
52                output = []
53            output += [part for part in line.split(' ') if part.strip()]
54            return output
55
56        return [
57            IntegratedTestKeywordParser("MY_TAG.", ParserKind.TAG),
58            IntegratedTestKeywordParser("MY_DNE_TAG.", ParserKind.TAG),
59            IntegratedTestKeywordParser("MY_LIST:", ParserKind.LIST),
60            IntegratedTestKeywordParser("MY_BOOL:", ParserKind.BOOLEAN_EXPR),
61            IntegratedTestKeywordParser("MY_INT:", ParserKind.INTEGER),
62            IntegratedTestKeywordParser("MY_RUN:", ParserKind.COMMAND),
63            IntegratedTestKeywordParser("MY_CUSTOM:", ParserKind.CUSTOM,
64                                        custom_parse),
65            IntegratedTestKeywordParser("MY_DEFINE:", ParserKind.DEFINE),
66            IntegratedTestKeywordParser("MY_REDEFINE:", ParserKind.REDEFINE),
67        ]
68
69    @staticmethod
70    def get_parser(parser_list, keyword):
71        for p in parser_list:
72            if p.keyword == keyword:
73                return p
74        assert False and "parser not found"
75
76    @staticmethod
77    def parse_test(parser_list, allow_result=False):
78        script = parseIntegratedTestScript(
79            TestIntegratedTestKeywordParser.inputTestCase,
80            additional_parsers=parser_list, require_script=False)
81        if isinstance(script, lit.Test.Result):
82            assert allow_result
83        else:
84            assert isinstance(script, list)
85            assert len(script) == 0
86        return script
87
88    def test_tags(self):
89        parsers = self.make_parsers()
90        self.parse_test(parsers)
91        tag_parser = self.get_parser(parsers, 'MY_TAG.')
92        dne_tag_parser = self.get_parser(parsers, 'MY_DNE_TAG.')
93        self.assertTrue(tag_parser.getValue())
94        self.assertFalse(dne_tag_parser.getValue())
95
96    def test_lists(self):
97        parsers = self.make_parsers()
98        self.parse_test(parsers)
99        list_parser = self.get_parser(parsers, 'MY_LIST:')
100        self.assertEqual(list_parser.getValue(),
101                              ['one', 'two', 'three', 'four'])
102
103    def test_commands(self):
104        parsers = self.make_parsers()
105        self.parse_test(parsers)
106        cmd_parser = self.get_parser(parsers, 'MY_RUN:')
107        value = cmd_parser.getValue()
108        self.assertEqual(len(value), 2)  # there are only two run lines
109        self.assertEqual(value[0].command.strip(),
110                         "%dbg(MY_RUN: at line 4)  baz")
111        self.assertEqual(value[1].command.strip(),
112                         "%dbg(MY_RUN: at line 7)  foo  bar")
113
114    def test_boolean(self):
115        parsers = self.make_parsers()
116        self.parse_test(parsers)
117        bool_parser = self.get_parser(parsers, 'MY_BOOL:')
118        value = bool_parser.getValue()
119        self.assertEqual(len(value), 2)  # there are only two run lines
120        self.assertEqual(value[0].strip(), "a && (b)")
121        self.assertEqual(value[1].strip(), "d")
122
123    def test_integer(self):
124        parsers = self.make_parsers()
125        self.parse_test(parsers)
126        int_parser = self.get_parser(parsers, 'MY_INT:')
127        value = int_parser.getValue()
128        self.assertEqual(len(value), 2)  # there are only two MY_INT: lines
129        self.assertEqual(type(value[0]), int)
130        self.assertEqual(value[0], 4)
131        self.assertEqual(type(value[1]), int)
132        self.assertEqual(value[1], 6)
133
134    def test_bad_parser_type(self):
135        parsers = self.make_parsers() + ["BAD_PARSER_TYPE"]
136        script = self.parse_test(parsers, allow_result=True)
137        self.assertTrue(isinstance(script, lit.Test.Result))
138        self.assertEqual(script.code, lit.Test.UNRESOLVED)
139        self.assertEqual('Additional parser must be an instance of '
140                         'IntegratedTestKeywordParser',
141                         script.output)
142
143    def test_duplicate_keyword(self):
144        parsers = self.make_parsers() + \
145            [IntegratedTestKeywordParser("KEY:", ParserKind.BOOLEAN_EXPR),
146             IntegratedTestKeywordParser("KEY:", ParserKind.BOOLEAN_EXPR)]
147        script = self.parse_test(parsers, allow_result=True)
148        self.assertTrue(isinstance(script, lit.Test.Result))
149        self.assertEqual(script.code, lit.Test.UNRESOLVED)
150        self.assertEqual("Parser for keyword 'KEY:' already exists",
151                         script.output)
152
153    def test_boolean_unterminated(self):
154        parsers = self.make_parsers() + \
155            [IntegratedTestKeywordParser("MY_BOOL_UNTERMINATED:", ParserKind.BOOLEAN_EXPR)]
156        script = self.parse_test(parsers, allow_result=True)
157        self.assertTrue(isinstance(script, lit.Test.Result))
158        self.assertEqual(script.code, lit.Test.UNRESOLVED)
159        self.assertEqual("Test has unterminated 'MY_BOOL_UNTERMINATED:' lines "
160                         "(with '\\')",
161                         script.output)
162
163    def test_custom(self):
164        parsers = self.make_parsers()
165        self.parse_test(parsers)
166        custom_parser = self.get_parser(parsers, 'MY_CUSTOM:')
167        value = custom_parser.getValue()
168        self.assertEqual(value, ['a', 'b', 'c'])
169
170    def test_defines(self):
171        parsers = self.make_parsers()
172        self.parse_test(parsers)
173        cmd_parser = self.get_parser(parsers, 'MY_DEFINE:')
174        value = cmd_parser.getValue()
175        self.assertEqual(len(value), 1) # there's only one MY_DEFINE directive
176        self.assertEqual(value[0].new_subst, True)
177        self.assertEqual(value[0].name, '%{name}')
178        self.assertEqual(value[0].value, 'value one')
179
180    def test_redefines(self):
181        parsers = self.make_parsers()
182        self.parse_test(parsers)
183        cmd_parser = self.get_parser(parsers, 'MY_REDEFINE:')
184        value = cmd_parser.getValue()
185        self.assertEqual(len(value), 1) # there's only one MY_REDEFINE directive
186        self.assertEqual(value[0].new_subst, False)
187        self.assertEqual(value[0].name, '%{name}')
188        self.assertEqual(value[0].value, 'value two')
189
190    def test_bad_keywords(self):
191        def custom_parse(line_number, line, output):
192            return output
193
194        try:
195            IntegratedTestKeywordParser("TAG_NO_SUFFIX", ParserKind.TAG),
196            self.fail("TAG_NO_SUFFIX failed to raise an exception")
197        except ValueError as e:
198            pass
199        except BaseException as e:
200            self.fail("TAG_NO_SUFFIX raised the wrong exception: %r" % e)
201
202        try:
203            IntegratedTestKeywordParser("TAG_WITH_COLON:", ParserKind.TAG),
204            self.fail("TAG_WITH_COLON: failed to raise an exception")
205        except ValueError as e:
206            pass
207        except BaseException as e:
208            self.fail("TAG_WITH_COLON: raised the wrong exception: %r" % e)
209
210        try:
211            IntegratedTestKeywordParser("LIST_WITH_DOT.", ParserKind.LIST),
212            self.fail("LIST_WITH_DOT. failed to raise an exception")
213        except ValueError as e:
214            pass
215        except BaseException as e:
216            self.fail("LIST_WITH_DOT. raised the wrong exception: %r" % e)
217
218        try:
219            IntegratedTestKeywordParser("CUSTOM_NO_SUFFIX",
220                                        ParserKind.CUSTOM, custom_parse),
221            self.fail("CUSTOM_NO_SUFFIX failed to raise an exception")
222        except ValueError as e:
223            pass
224        except BaseException as e:
225            self.fail("CUSTOM_NO_SUFFIX raised the wrong exception: %r" % e)
226
227        # Both '.' and ':' are allowed for CUSTOM keywords.
228        try:
229            IntegratedTestKeywordParser("CUSTOM_WITH_DOT.",
230                                        ParserKind.CUSTOM, custom_parse),
231        except BaseException as e:
232            self.fail("CUSTOM_WITH_DOT. raised an exception: %r" % e)
233        try:
234            IntegratedTestKeywordParser("CUSTOM_WITH_COLON:",
235                                        ParserKind.CUSTOM, custom_parse),
236        except BaseException as e:
237            self.fail("CUSTOM_WITH_COLON: raised an exception: %r" % e)
238
239        try:
240            IntegratedTestKeywordParser("CUSTOM_NO_PARSER:",
241                                        ParserKind.CUSTOM),
242            self.fail("CUSTOM_NO_PARSER: failed to raise an exception")
243        except ValueError as e:
244            pass
245        except BaseException as e:
246            self.fail("CUSTOM_NO_PARSER: raised the wrong exception: %r" % e)
247
248class TestApplySubtitutions(unittest.TestCase):
249    def test_simple(self):
250        script = ["echo %bar"]
251        substitutions = [("%bar", "hello")]
252        result = lit.TestRunner.applySubstitutions(script, substitutions)
253        self.assertEqual(result, ["echo hello"])
254
255    def test_multiple_substitutions(self):
256        script = ["echo %bar %baz"]
257        substitutions = [("%bar", "hello"),
258                         ("%baz", "world"),
259                         ("%useless", "shouldnt expand")]
260        result = lit.TestRunner.applySubstitutions(script, substitutions)
261        self.assertEqual(result, ["echo hello world"])
262
263    def test_multiple_script_lines(self):
264        script = ["%cxx %compile_flags -c -o %t.o",
265                  "%cxx %link_flags %t.o -o %t.exe"]
266        substitutions = [("%cxx", "clang++"),
267                         ("%compile_flags", "-std=c++11 -O3"),
268                         ("%link_flags", "-lc++")]
269        result = lit.TestRunner.applySubstitutions(script, substitutions)
270        self.assertEqual(result, ["clang++ -std=c++11 -O3 -c -o %t.o",
271                                  "clang++ -lc++ %t.o -o %t.exe"])
272
273    def test_recursive_substitution_real(self):
274        script = ["%build %s"]
275        substitutions = [("%cxx", "clang++"),
276                         ("%compile_flags", "-std=c++11 -O3"),
277                         ("%link_flags", "-lc++"),
278                         ("%build", "%cxx %compile_flags %link_flags %s -o %t.exe")]
279        result = lit.TestRunner.applySubstitutions(script, substitutions, recursion_limit=3)
280        self.assertEqual(result, ["clang++ -std=c++11 -O3 -lc++ %s -o %t.exe %s"])
281
282    def test_recursive_substitution_limit(self):
283        script = ["%rec5"]
284        # Make sure the substitutions are not in an order where the global
285        # substitution would appear to be recursive just because they are
286        # processed in the right order.
287        substitutions = [("%rec1", "STOP"), ("%rec2", "%rec1"),
288                         ("%rec3", "%rec2"), ("%rec4", "%rec3"), ("%rec5", "%rec4")]
289        for limit in [5, 6, 7]:
290            result = lit.TestRunner.applySubstitutions(script, substitutions, recursion_limit=limit)
291            self.assertEqual(result, ["STOP"])
292
293    def test_recursive_substitution_limit_exceeded(self):
294        script = ["%rec5"]
295        substitutions = [("%rec1", "STOP"), ("%rec2", "%rec1"),
296                         ("%rec3", "%rec2"), ("%rec4", "%rec3"), ("%rec5", "%rec4")]
297        for limit in [0, 1, 2, 3, 4]:
298            try:
299                lit.TestRunner.applySubstitutions(script, substitutions, recursion_limit=limit)
300                self.fail("applySubstitutions should have raised an exception")
301            except ValueError:
302                pass
303
304    def test_recursive_substitution_invalid_value(self):
305        script = ["%rec5"]
306        substitutions = [("%rec1", "STOP"), ("%rec2", "%rec1"),
307                         ("%rec3", "%rec2"), ("%rec4", "%rec3"), ("%rec5", "%rec4")]
308        for limit in [-1, -2, -3, "foo"]:
309            try:
310                lit.TestRunner.applySubstitutions(script, substitutions, recursion_limit=limit)
311                self.fail("applySubstitutions should have raised an exception")
312            except AssertionError:
313                pass
314
315
316if __name__ == '__main__':
317    TestIntegratedTestKeywordParser.load_keyword_parser_lit_tests()
318    unittest.main(verbosity=2)
319