summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlmr <lmr@592f7852-d20e-0410-864c-8624ca9c26a4>2011-06-25 17:19:16 +0000
committerlmr <lmr@592f7852-d20e-0410-864c-8624ca9c26a4>2011-06-25 17:19:16 +0000
commit8ea2aa8ff02426b3b248a7e3d48efb215a55904b (patch)
treebd1f47abab88b59085ae5b9480d2b3cf886f9b85
parentb0b45fb92c6f79af2b1e2bce8c332e4f19d4d19b (diff)
tko.parsers: Support multiline, tabbed reasons. Improve parser robustness.HEADmaster
Allowing reasons with multiple lines and tabs provides more flexibility in error messages. The parser is also more robust, as any invalid fields are moved into the reason and can be fixed offline without impacting the rest of the system. Signed-off-by: Dale Curtis <dalecurtis@google.com> git-svn-id: svn://test.kernel.org/autotest/trunk@5452 592f7852-d20e-0410-864c-8624ca9c26a4
-rw-r--r--tko/parsers/version_0.py28
-rwxr-xr-xtko/parsers/version_0_unittest.py32
-rwxr-xr-xtko/parsers/version_1_unittest.py7
3 files changed, 45 insertions, 22 deletions
diff --git a/tko/parsers/version_0.py b/tko/parsers/version_0.py
index c690d211..ed12cb48 100644
--- a/tko/parsers/version_0.py
+++ b/tko/parsers/version_0.py
@@ -251,7 +251,7 @@ class status_line(object):
def parse_line(cls, line):
if not status_line.is_status_line(line):
return None
- match = re.search(r"^(\t*)(.*)$", line)
+ match = re.search(r"^(\t*)(.*)$", line, flags=re.DOTALL)
if not match:
# A more useful error message than:
# AttributeError: 'NoneType' object has no attribute 'groups'
@@ -261,15 +261,23 @@ class status_line(object):
indent = len(indent)
# split the line into the fixed and optional fields
- parts = line.split("\t")
- status, subdir, testname = parts[0:3]
- reason = parts[-1]
- optional_parts = parts[3:-1]
-
- # all the optional parts should be of the form "key=value"
- assert sum('=' not in part for part in optional_parts) == 0
- optional_fields = dict(part.split("=", 1)
- for part in optional_parts)
+ parts = line.rstrip("\n").split("\t")
+
+ part_index = 3
+ status, subdir, testname = parts[0:part_index]
+
+ # all optional parts should be of the form "key=value". once we've found
+ # a non-matching part, treat it and the rest of the parts as the reason.
+ optional_fields = {}
+ while part_index < len(parts):
+ kv = parts[part_index].split('=', 1)
+ if len(kv) < 2:
+ break
+
+ optional_fields[kv[0]] = kv[1]
+ part_index += 1
+
+ reason = "\t".join(parts[part_index:])
# build up a new status_line and return it
return cls(indent, status, subdir, testname, reason,
diff --git a/tko/parsers/version_0_unittest.py b/tko/parsers/version_0_unittest.py
index 4807beec..286e5bb2 100755
--- a/tko/parsers/version_0_unittest.py
+++ b/tko/parsers/version_0_unittest.py
@@ -230,6 +230,20 @@ class test_status_line(unittest.TestCase):
"field2": "val2"})
+ def test_parse_line_handles_embedded_new_lines(self):
+ input_data = ("\tEND FAIL\t----\ttest\tfield1=val1\tStatus\nwith\n"
+ "embedded\nnew lines\n")
+
+ line = version_0.status_line.parse_line(input_data)
+ self.assertEquals(line.indent, 1)
+ self.assertEquals(line.type, "END")
+ self.assertEquals(line.status, "FAIL")
+ self.assertEquals(line.subdir, None)
+ self.assertEquals(line.testname, "test")
+ self.assertEquals(line.reason, "Status\nwith\nembedded\nnew lines")
+ self.assertEquals(line.optional_fields, {"field1": "val1"})
+
+
def test_parse_line_fails_on_untabbed_lines(self):
input_data = " GOOD\trandom\tfields\tof text"
line = version_0.status_line.parse_line(input_data)
@@ -259,11 +273,19 @@ class test_status_line(unittest.TestCase):
self.assertEquals(line.optional_fields, {})
- def test_parse_line_fails_on_bad_optional_fields(self):
- input_data = "GOOD\tfield1\tfield2\tfield3\tfield4"
- self.assertRaises(AssertionError,
- version_0.status_line.parse_line,
- input_data)
+ def test_parse_line_handles_tabs_in_reason(self):
+ input_data = ("\tEND FAIL\t----\ttest\tfield1=val1\tfield2=val2\tReason"
+ " with\ta\tcouple\ttabs")
+
+ line = version_0.status_line.parse_line(input_data)
+ self.assertEquals(line.indent, 1)
+ self.assertEquals(line.type, "END")
+ self.assertEquals(line.status, "FAIL")
+ self.assertEquals(line.subdir, None)
+ self.assertEquals(line.testname, "test")
+ self.assertEquals(line.reason, "Reason with\ta\tcouple\ttabs")
+ self.assertEquals(line.optional_fields, {"field1": "val1",
+ "field2": "val2"})
if __name__ == "__main__":
diff --git a/tko/parsers/version_1_unittest.py b/tko/parsers/version_1_unittest.py
index 4114c59c..d5693663 100755
--- a/tko/parsers/version_1_unittest.py
+++ b/tko/parsers/version_1_unittest.py
@@ -136,13 +136,6 @@ class test_status_line(unittest.TestCase):
self.assertEquals(line.optional_fields, {})
- def test_parse_line_fails_on_bad_optional_fields(self):
- input_data = "GOOD\tfield1\tfield2\tfield3\tfield4"
- self.assertRaises(AssertionError,
- version_1.status_line.parse_line,
- input_data)
-
-
def test_good_reboot_passes_success_test(self):
line = version_1.status_line(0, "NOSTATUS", None, "reboot",
"reboot success", {})