[llvm] 2866f9d - [lit] Fix handling of various keyword parse errors

Joel E. Denny via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 12 06:38:47 PDT 2020


Author: Joel E. Denny
Date: 2020-06-12T09:37:40-04:00
New Revision: 2866f9db9e5e9d2e6c14ae179c49c2f1a35bca90

URL: https://github.com/llvm/llvm-project/commit/2866f9db9e5e9d2e6c14ae179c49c2f1a35bca90
DIFF: https://github.com/llvm/llvm-project/commit/2866f9db9e5e9d2e6c14ae179c49c2f1a35bca90.diff

LOG: [lit] Fix handling of various keyword parse errors

In TestRunner.py, D78589 extracts a `_parseKeywords` function from
`parseIntegratedTestScript`, which then expects `_parseKeywords` to
always return a list of keyword/value pairs.  However, the extracted
code sometimes returns an unresolved `lit.Test.Result` on a keyword
parsing error, which then produces a stack dump instead of the
expected diagnostic.

This patch fixes that, makes the style of those diagnostics more
consistent, and extends the lit test suite to cover them.

Reviewed By: ldionne

Differential Revision: https://reviews.llvm.org/D81665

Added: 
    llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/empty.txt
    llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/lit.cfg
    llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/multiple-allow-retries.txt
    llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/unterminated-run.txt
    llvm/utils/lit/tests/shtest-keyword-parse-errors.py

Modified: 
    llvm/utils/lit/lit/TestRunner.py
    llvm/utils/lit/tests/unit/TestRunner.py

Removed: 
    


################################################################################
diff  --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py
index edcb696a8b16..ab557b767b03 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -1401,7 +1401,7 @@ def _parseKeywords(sourcepath, additional_parsers=[],
     # Install user-defined additional parsers.
     for parser in additional_parsers:
         if not isinstance(parser, IntegratedTestKeywordParser):
-            raise ValueError('additional parser must be an instance of '
+            raise ValueError('Additional parser must be an instance of '
                              'IntegratedTestKeywordParser')
         if parser.keyword in keyword_parsers:
             raise ValueError("Parser for keyword '%s' already exists"
@@ -1419,12 +1419,11 @@ def _parseKeywords(sourcepath, additional_parsers=[],
 
     # Verify the script contains a run line.
     if require_script and not script:
-        return lit.Test.Result(Test.UNRESOLVED, "Test has no run line!")
+        raise ValueError("Test has no 'RUN:' line")
 
     # Check for unterminated run lines.
     if script and script[-1][-1] == '\\':
-        return lit.Test.Result(Test.UNRESOLVED,
-                               "Test has unterminated run lines (with '\\')")
+        raise ValueError("Test has unterminated 'RUN:' lines (with '\\')")
 
     # Check boolean expressions for unterminated lines.
     for key in keyword_parsers:
@@ -1433,13 +1432,13 @@ def _parseKeywords(sourcepath, additional_parsers=[],
             continue
         value = kp.getValue()
         if value and value[-1][-1] == '\\':
-            raise ValueError("Test has unterminated %s lines (with '\\')" % key)
+            raise ValueError("Test has unterminated '{key}' lines (with '\\')"
+                             .format(key=key))
 
     # Make sure there's at most one ALLOW_RETRIES: line
     allowed_retries = keyword_parsers['ALLOW_RETRIES:'].getValue()
     if allowed_retries and len(allowed_retries) > 1:
-        return lit.Test.Result(Test.UNRESOLVED,
-                               "Test has more than one ALLOW_RETRIES lines")
+        raise ValueError("Test has more than one ALLOW_RETRIES lines")
 
     return {p.keyword: p.getValue() for p in keyword_parsers.values()}
 
@@ -1458,7 +1457,11 @@ def parseIntegratedTestScript(test, additional_parsers=[],
     is optional or ignored.
     """
     # Parse the test sources and extract test properties
-    parsed = _parseKeywords(test.getSourcePath(), additional_parsers, require_script)
+    try:
+        parsed = _parseKeywords(test.getSourcePath(), additional_parsers,
+                                require_script)
+    except ValueError as e:
+        return lit.Test.Result(Test.UNRESOLVED, str(e))
     script = parsed['RUN:'] or []
     test.xfails = parsed['XFAIL:'] or []
     test.requires = parsed['REQUIRES:'] or []

diff  --git a/llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/empty.txt b/llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/empty.txt
new file mode 100644
index 000000000000..e69de29bb2d1

diff  --git a/llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/lit.cfg b/llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/lit.cfg
new file mode 100644
index 000000000000..5a28fb03ece3
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/lit.cfg
@@ -0,0 +1,4 @@
+import lit.formats
+config.name = 'shtest-keyword-parse-errors'
+config.suffixes = ['.txt']
+config.test_format = lit.formats.ShTest()

diff  --git a/llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/multiple-allow-retries.txt b/llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/multiple-allow-retries.txt
new file mode 100644
index 000000000000..151dcdfd3e66
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/multiple-allow-retries.txt
@@ -0,0 +1,3 @@
+ALLOW_RETRIES: 1
+ALLOW_RETRIES: 1
+RUN: echo

diff  --git a/llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/unterminated-run.txt b/llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/unterminated-run.txt
new file mode 100644
index 000000000000..38b53b8ada95
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-keyword-parse-errors/unterminated-run.txt
@@ -0,0 +1,3 @@
+# RUN: echo \
+
+Program code.

diff  --git a/llvm/utils/lit/tests/shtest-keyword-parse-errors.py b/llvm/utils/lit/tests/shtest-keyword-parse-errors.py
new file mode 100644
index 000000000000..fb652bd5964d
--- /dev/null
+++ b/llvm/utils/lit/tests/shtest-keyword-parse-errors.py
@@ -0,0 +1,15 @@
+# RUN: not %{lit} -j 1 -vv %{inputs}/shtest-keyword-parse-errors > %t.out
+# RUN: FileCheck -input-file %t.out %s
+#
+# END.
+
+# CHECK: Testing: 3 tests
+
+# CHECK-LABEL: UNRESOLVED: shtest-keyword-parse-errors :: empty.txt
+# CHECK:       {{^}}Test has no 'RUN:' line{{$}}
+
+# CHECK-LABEL: UNRESOLVED: shtest-keyword-parse-errors :: multiple-allow-retries.txt
+# CHECK:       {{^}}Test has more than one ALLOW_RETRIES lines{{$}}
+
+# CHECK-LABEL: UNRESOLVED: shtest-keyword-parse-errors :: unterminated-run.txt
+# CHECK:       {{^}}Test has unterminated 'RUN:' lines (with '\'){{$}}

diff  --git a/llvm/utils/lit/tests/unit/TestRunner.py b/llvm/utils/lit/tests/unit/TestRunner.py
index 5b5703c2e17d..33e4ebc716f4 100644
--- a/llvm/utils/lit/tests/unit/TestRunner.py
+++ b/llvm/utils/lit/tests/unit/TestRunner.py
@@ -72,13 +72,16 @@ def get_parser(parser_list, keyword):
         assert False and "parser not found"
 
     @staticmethod
-    def parse_test(parser_list):
+    def parse_test(parser_list, allow_result=False):
         script = parseIntegratedTestScript(
             TestIntegratedTestKeywordParser.inputTestCase,
             additional_parsers=parser_list, require_script=False)
-        assert not isinstance(script, lit.Test.Result)
-        assert isinstance(script, list)
-        assert len(script) == 0
+        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()
@@ -124,15 +127,34 @@ def test_integer(self):
         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)]
-        try:
-            self.parse_test(parsers)
-            self.fail('expected exception')
-        except ValueError as e:
-            self.assertIn("Test has unterminated MY_BOOL_UNTERMINATED: lines", str(e))
-
+        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()


        


More information about the llvm-commits mailing list