|  | # RUN: %{python} %s | 
|  | # | 
|  | # END. | 
|  |  | 
|  |  | 
|  | import os.path | 
|  | import platform | 
|  | import unittest | 
|  |  | 
|  | import lit.discovery | 
|  | import lit.LitConfig | 
|  | import lit.Test as Test | 
|  | from lit.TestRunner import ( | 
|  | ParserKind, | 
|  | IntegratedTestKeywordParser, | 
|  | parseIntegratedTestScript, | 
|  | ) | 
|  |  | 
|  |  | 
|  | class TestIntegratedTestKeywordParser(unittest.TestCase): | 
|  | inputTestCase = None | 
|  |  | 
|  | @staticmethod | 
|  | def load_keyword_parser_lit_tests(): | 
|  | """ | 
|  | Create and load the LIT test suite and test objects used by | 
|  | TestIntegratedTestKeywordParser | 
|  | """ | 
|  | # Create the global config object. | 
|  | lit_config = lit.LitConfig.LitConfig( | 
|  | progname="lit", | 
|  | path=[], | 
|  | quiet=False, | 
|  | useValgrind=False, | 
|  | valgrindLeakCheck=False, | 
|  | valgrindArgs=[], | 
|  | noExecute=False, | 
|  | debug=False, | 
|  | isWindows=(platform.system() == "Windows"), | 
|  | order="smart", | 
|  | params={}, | 
|  | ) | 
|  | TestIntegratedTestKeywordParser.litConfig = lit_config | 
|  | # Perform test discovery. | 
|  | test_path = os.path.dirname(os.path.dirname(__file__)) | 
|  | inputs = [os.path.join(test_path, "Inputs/testrunner-custom-parsers/")] | 
|  | assert os.path.isdir(inputs[0]) | 
|  | tests = lit.discovery.find_tests_for_inputs(lit_config, inputs) | 
|  | assert len(tests) == 1 and "there should only be one test" | 
|  | TestIntegratedTestKeywordParser.inputTestCase = tests[0] | 
|  |  | 
|  | @staticmethod | 
|  | def make_parsers(): | 
|  | def custom_parse(line_number, line, output): | 
|  | if output is None: | 
|  | output = [] | 
|  | output += [part for part in line.split(" ") if part.strip()] | 
|  | return output | 
|  |  | 
|  | return [ | 
|  | IntegratedTestKeywordParser("MY_TAG.", ParserKind.TAG), | 
|  | IntegratedTestKeywordParser("MY_DNE_TAG.", ParserKind.TAG), | 
|  | IntegratedTestKeywordParser("MY_LIST:", ParserKind.LIST), | 
|  | IntegratedTestKeywordParser("MY_SPACE_LIST:", ParserKind.SPACE_LIST), | 
|  | IntegratedTestKeywordParser("MY_BOOL:", ParserKind.BOOLEAN_EXPR), | 
|  | IntegratedTestKeywordParser("MY_INT:", ParserKind.INTEGER), | 
|  | IntegratedTestKeywordParser("MY_RUN:", ParserKind.COMMAND), | 
|  | IntegratedTestKeywordParser("MY_CUSTOM:", ParserKind.CUSTOM, custom_parse), | 
|  | IntegratedTestKeywordParser("MY_DEFINE:", ParserKind.DEFINE), | 
|  | IntegratedTestKeywordParser("MY_REDEFINE:", ParserKind.REDEFINE), | 
|  | ] | 
|  |  | 
|  | @staticmethod | 
|  | def get_parser(parser_list, keyword): | 
|  | for p in parser_list: | 
|  | if p.keyword == keyword: | 
|  | return p | 
|  | assert False and "parser not found" | 
|  |  | 
|  | @staticmethod | 
|  | def parse_test(parser_list, allow_result=False): | 
|  | script = parseIntegratedTestScript( | 
|  | TestIntegratedTestKeywordParser.inputTestCase, | 
|  | additional_parsers=parser_list, | 
|  | require_script=False, | 
|  | ) | 
|  | if isinstance(script, lit.Test.Result): | 
|  | assert allow_result | 
|  | else: | 
|  | assert isinstance(script, list) | 
|  | assert len(script) == 0 | 
|  | return script | 
|  |  | 
|  | def test_tags(self): | 
|  | parsers = self.make_parsers() | 
|  | self.parse_test(parsers) | 
|  | tag_parser = self.get_parser(parsers, "MY_TAG.") | 
|  | dne_tag_parser = self.get_parser(parsers, "MY_DNE_TAG.") | 
|  | self.assertTrue(tag_parser.getValue()) | 
|  | self.assertFalse(dne_tag_parser.getValue()) | 
|  |  | 
|  | def test_lists(self): | 
|  | parsers = self.make_parsers() | 
|  | self.parse_test(parsers) | 
|  | list_parser = self.get_parser(parsers, "MY_LIST:") | 
|  | self.assertEqual(list_parser.getValue(), ["one", "two", "three", "four"]) | 
|  |  | 
|  | def test_space_lists(self): | 
|  | parsers = self.make_parsers() | 
|  | self.parse_test(parsers) | 
|  | space_list_parser = self.get_parser(parsers, "MY_SPACE_LIST:") | 
|  | self.assertEqual( | 
|  | space_list_parser.getValue(), | 
|  | [ | 
|  | "orange", | 
|  | "tabby", | 
|  | "tortie", | 
|  | "tuxedo", | 
|  | "void", | 
|  | "multiple", | 
|  | "spaces", | 
|  | "cute,", | 
|  | "fluffy,", | 
|  | "kittens", | 
|  | ], | 
|  | ) | 
|  |  | 
|  | def test_commands(self): | 
|  | parsers = self.make_parsers() | 
|  | self.parse_test(parsers) | 
|  | cmd_parser = self.get_parser(parsers, "MY_RUN:") | 
|  | value = cmd_parser.getValue() | 
|  | self.assertEqual(len(value), 2)  # there are only two run lines | 
|  | self.assertEqual(value[0].command.strip(), "%dbg(MY_RUN: at line 4)  baz") | 
|  | self.assertEqual(value[1].command.strip(), "%dbg(MY_RUN: at line 12)  foo  bar") | 
|  |  | 
|  | def test_boolean(self): | 
|  | parsers = self.make_parsers() | 
|  | self.parse_test(parsers) | 
|  | bool_parser = self.get_parser(parsers, "MY_BOOL:") | 
|  | value = bool_parser.getValue() | 
|  | self.assertEqual(len(value), 2)  # there are only two run lines | 
|  | self.assertEqual(value[0].strip(), "a && (b)") | 
|  | self.assertEqual(value[1].strip(), "d") | 
|  |  | 
|  | def test_integer(self): | 
|  | parsers = self.make_parsers() | 
|  | self.parse_test(parsers) | 
|  | int_parser = self.get_parser(parsers, "MY_INT:") | 
|  | value = int_parser.getValue() | 
|  | self.assertEqual(len(value), 2)  # there are only two MY_INT: lines | 
|  | self.assertEqual(type(value[0]), int) | 
|  | self.assertEqual(value[0], 4) | 
|  | self.assertEqual(type(value[1]), int) | 
|  | self.assertEqual(value[1], 6) | 
|  |  | 
|  | def test_bad_parser_type(self): | 
|  | parsers = self.make_parsers() + ["BAD_PARSER_TYPE"] | 
|  | script = self.parse_test(parsers, allow_result=True) | 
|  | self.assertTrue(isinstance(script, lit.Test.Result)) | 
|  | self.assertEqual(script.code, lit.Test.UNRESOLVED) | 
|  | self.assertEqual( | 
|  | "Additional parser must be an instance of " "IntegratedTestKeywordParser", | 
|  | script.output, | 
|  | ) | 
|  |  | 
|  | def test_duplicate_keyword(self): | 
|  | parsers = self.make_parsers() + [ | 
|  | IntegratedTestKeywordParser("KEY:", ParserKind.BOOLEAN_EXPR), | 
|  | IntegratedTestKeywordParser("KEY:", ParserKind.BOOLEAN_EXPR), | 
|  | ] | 
|  | script = self.parse_test(parsers, allow_result=True) | 
|  | self.assertTrue(isinstance(script, lit.Test.Result)) | 
|  | self.assertEqual(script.code, lit.Test.UNRESOLVED) | 
|  | self.assertEqual("Parser for keyword 'KEY:' already exists", script.output) | 
|  |  | 
|  | def test_boolean_unterminated(self): | 
|  | parsers = self.make_parsers() + [ | 
|  | IntegratedTestKeywordParser( | 
|  | "MY_BOOL_UNTERMINATED:", ParserKind.BOOLEAN_EXPR | 
|  | ) | 
|  | ] | 
|  | script = self.parse_test(parsers, allow_result=True) | 
|  | self.assertTrue(isinstance(script, lit.Test.Result)) | 
|  | self.assertEqual(script.code, lit.Test.UNRESOLVED) | 
|  | self.assertEqual( | 
|  | "Test has unterminated 'MY_BOOL_UNTERMINATED:' lines " "(with '\\')", | 
|  | script.output, | 
|  | ) | 
|  |  | 
|  | def test_custom(self): | 
|  | parsers = self.make_parsers() | 
|  | self.parse_test(parsers) | 
|  | custom_parser = self.get_parser(parsers, "MY_CUSTOM:") | 
|  | value = custom_parser.getValue() | 
|  | self.assertEqual(value, ["a", "b", "c"]) | 
|  |  | 
|  | def test_defines(self): | 
|  | parsers = self.make_parsers() | 
|  | self.parse_test(parsers) | 
|  | cmd_parser = self.get_parser(parsers, "MY_DEFINE:") | 
|  | value = cmd_parser.getValue() | 
|  | self.assertEqual(len(value), 1)  # there's only one MY_DEFINE directive | 
|  | self.assertEqual(value[0].new_subst, True) | 
|  | self.assertEqual(value[0].name, "%{name}") | 
|  | self.assertEqual(value[0].value, "value one") | 
|  |  | 
|  | def test_redefines(self): | 
|  | parsers = self.make_parsers() | 
|  | self.parse_test(parsers) | 
|  | cmd_parser = self.get_parser(parsers, "MY_REDEFINE:") | 
|  | value = cmd_parser.getValue() | 
|  | self.assertEqual(len(value), 1)  # there's only one MY_REDEFINE directive | 
|  | self.assertEqual(value[0].new_subst, False) | 
|  | self.assertEqual(value[0].name, "%{name}") | 
|  | self.assertEqual(value[0].value, "value two") | 
|  |  | 
|  | def test_bad_keywords(self): | 
|  | def custom_parse(line_number, line, output): | 
|  | return output | 
|  |  | 
|  | try: | 
|  | IntegratedTestKeywordParser("TAG_NO_SUFFIX", ParserKind.TAG), | 
|  | self.fail("TAG_NO_SUFFIX failed to raise an exception") | 
|  | except ValueError as e: | 
|  | pass | 
|  | except BaseException as e: | 
|  | self.fail("TAG_NO_SUFFIX raised the wrong exception: %r" % e) | 
|  |  | 
|  | try: | 
|  | IntegratedTestKeywordParser("TAG_WITH_COLON:", ParserKind.TAG), | 
|  | self.fail("TAG_WITH_COLON: failed to raise an exception") | 
|  | except ValueError as e: | 
|  | pass | 
|  | except BaseException as e: | 
|  | self.fail("TAG_WITH_COLON: raised the wrong exception: %r" % e) | 
|  |  | 
|  | try: | 
|  | IntegratedTestKeywordParser("LIST_WITH_DOT.", ParserKind.LIST), | 
|  | self.fail("LIST_WITH_DOT. failed to raise an exception") | 
|  | except ValueError as e: | 
|  | pass | 
|  | except BaseException as e: | 
|  | self.fail("LIST_WITH_DOT. raised the wrong exception: %r" % e) | 
|  |  | 
|  | try: | 
|  | IntegratedTestKeywordParser("SPACE_LIST_WITH_DOT.", ParserKind.SPACE_LIST), | 
|  | self.fail("SPACE_LIST_WITH_DOT. failed to raise an exception") | 
|  | except ValueError as e: | 
|  | pass | 
|  | except BaseException as e: | 
|  | self.fail("SPACE_LIST_WITH_DOT. raised the wrong exception: %r" % e) | 
|  |  | 
|  | try: | 
|  | IntegratedTestKeywordParser( | 
|  | "CUSTOM_NO_SUFFIX", ParserKind.CUSTOM, custom_parse | 
|  | ), | 
|  | self.fail("CUSTOM_NO_SUFFIX failed to raise an exception") | 
|  | except ValueError as e: | 
|  | pass | 
|  | except BaseException as e: | 
|  | self.fail("CUSTOM_NO_SUFFIX raised the wrong exception: %r" % e) | 
|  |  | 
|  | # Both '.' and ':' are allowed for CUSTOM keywords. | 
|  | try: | 
|  | IntegratedTestKeywordParser( | 
|  | "CUSTOM_WITH_DOT.", ParserKind.CUSTOM, custom_parse | 
|  | ), | 
|  | except BaseException as e: | 
|  | self.fail("CUSTOM_WITH_DOT. raised an exception: %r" % e) | 
|  | try: | 
|  | IntegratedTestKeywordParser( | 
|  | "CUSTOM_WITH_COLON:", ParserKind.CUSTOM, custom_parse | 
|  | ), | 
|  | except BaseException as e: | 
|  | self.fail("CUSTOM_WITH_COLON: raised an exception: %r" % e) | 
|  |  | 
|  | try: | 
|  | IntegratedTestKeywordParser("CUSTOM_NO_PARSER:", ParserKind.CUSTOM), | 
|  | self.fail("CUSTOM_NO_PARSER: failed to raise an exception") | 
|  | except ValueError as e: | 
|  | pass | 
|  | except BaseException as e: | 
|  | self.fail("CUSTOM_NO_PARSER: raised the wrong exception: %r" % e) | 
|  |  | 
|  |  | 
|  | class TestApplySubtitutions(unittest.TestCase): | 
|  | def test_simple(self): | 
|  | script = ["echo %bar"] | 
|  | substitutions = [("%bar", "hello")] | 
|  | result = lit.TestRunner.applySubstitutions(script, substitutions) | 
|  | self.assertEqual(result, ["echo hello"]) | 
|  |  | 
|  | def test_multiple_substitutions(self): | 
|  | script = ["echo %bar %baz"] | 
|  | substitutions = [ | 
|  | ("%bar", "hello"), | 
|  | ("%baz", "world"), | 
|  | ("%useless", "shouldnt expand"), | 
|  | ] | 
|  | result = lit.TestRunner.applySubstitutions(script, substitutions) | 
|  | self.assertEqual(result, ["echo hello world"]) | 
|  |  | 
|  | def test_multiple_script_lines(self): | 
|  | script = ["%cxx %compile_flags -c -o %t.o", "%cxx %link_flags %t.o -o %t.exe"] | 
|  | substitutions = [ | 
|  | ("%cxx", "clang++"), | 
|  | ("%compile_flags", "-std=c++11 -O3"), | 
|  | ("%link_flags", "-lc++"), | 
|  | ] | 
|  | result = lit.TestRunner.applySubstitutions(script, substitutions) | 
|  | self.assertEqual( | 
|  | result, | 
|  | ["clang++ -std=c++11 -O3 -c -o %t.o", "clang++ -lc++ %t.o -o %t.exe"], | 
|  | ) | 
|  |  | 
|  | def test_recursive_substitution_real(self): | 
|  | script = ["%build %s"] | 
|  | substitutions = [ | 
|  | ("%cxx", "clang++"), | 
|  | ("%compile_flags", "-std=c++11 -O3"), | 
|  | ("%link_flags", "-lc++"), | 
|  | ("%build", "%cxx %compile_flags %link_flags %s -o %t.exe"), | 
|  | ] | 
|  | result = lit.TestRunner.applySubstitutions( | 
|  | script, substitutions, recursion_limit=3 | 
|  | ) | 
|  | self.assertEqual(result, ["clang++ -std=c++11 -O3 -lc++ %s -o %t.exe %s"]) | 
|  |  | 
|  | def test_recursive_substitution_limit(self): | 
|  | script = ["%rec5"] | 
|  | # Make sure the substitutions are not in an order where the global | 
|  | # substitution would appear to be recursive just because they are | 
|  | # processed in the right order. | 
|  | substitutions = [ | 
|  | ("%rec1", "STOP"), | 
|  | ("%rec2", "%rec1"), | 
|  | ("%rec3", "%rec2"), | 
|  | ("%rec4", "%rec3"), | 
|  | ("%rec5", "%rec4"), | 
|  | ] | 
|  | for limit in [5, 6, 7]: | 
|  | result = lit.TestRunner.applySubstitutions( | 
|  | script, substitutions, recursion_limit=limit | 
|  | ) | 
|  | self.assertEqual(result, ["STOP"]) | 
|  |  | 
|  | def test_recursive_substitution_limit_exceeded(self): | 
|  | script = ["%rec5"] | 
|  | substitutions = [ | 
|  | ("%rec1", "STOP"), | 
|  | ("%rec2", "%rec1"), | 
|  | ("%rec3", "%rec2"), | 
|  | ("%rec4", "%rec3"), | 
|  | ("%rec5", "%rec4"), | 
|  | ] | 
|  | for limit in [0, 1, 2, 3, 4]: | 
|  | try: | 
|  | lit.TestRunner.applySubstitutions( | 
|  | script, substitutions, recursion_limit=limit | 
|  | ) | 
|  | self.fail("applySubstitutions should have raised an exception") | 
|  | except ValueError: | 
|  | pass | 
|  |  | 
|  | def test_recursive_substitution_invalid_value(self): | 
|  | script = ["%rec5"] | 
|  | substitutions = [ | 
|  | ("%rec1", "STOP"), | 
|  | ("%rec2", "%rec1"), | 
|  | ("%rec3", "%rec2"), | 
|  | ("%rec4", "%rec3"), | 
|  | ("%rec5", "%rec4"), | 
|  | ] | 
|  | for limit in [-1, -2, -3, "foo"]: | 
|  | try: | 
|  | lit.TestRunner.applySubstitutions( | 
|  | script, substitutions, recursion_limit=limit | 
|  | ) | 
|  | self.fail("applySubstitutions should have raised an exception") | 
|  | except AssertionError: | 
|  | pass | 
|  |  | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | TestIntegratedTestKeywordParser.load_keyword_parser_lit_tests() | 
|  | unittest.main(verbosity=2) |