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