提交 142189f0 编写于 作者: D Daniel Latypov 提交者: Shuah Khan

kunit: tool: print parsed test results fully incrementally

With the parser rework [1] and run_kernel() rework [2], this allows the
parser to print out test results incrementally.

Currently, that's held up by the fact that the LineStream eagerly
pre-fetches the next line when you call pop().
This blocks parse_test_result() from returning until the line *after*
the "ok 1 - test name" line is also printed.

One can see this with the following example:
$ (echo -e 'TAP version 14\n1..3\nok 1 - fake test'; sleep 2; echo -e 'ok 2 - fake test 2'; sleep 3; echo -e 'ok 3 - fake test 3') | ./tools/testing/kunit/kunit.py parse

Before this patch [1]: there's a pause before 'fake test' is printed.
After this patch: 'fake test' is printed out immediately.

This patch also adds
* a unit test to verify LineStream's behavior directly
* a test case to ensure that it's lazily calling the generator
* an explicit exception for when users go beyond EOF

[1] https://lore.kernel.org/linux-kselftest/20211006170049.106852-1-dlatypov@google.com/
[2] https://lore.kernel.org/linux-kselftest/20211005011340.2826268-1-dlatypov@google.com/Signed-off-by: NDaniel Latypov <dlatypov@google.com>
Reviewed-by: NDavid Gow <davidgow@google.com>
Reviewed-by: NBrendan Higgins <brendanhiggins@google.com>
Signed-off-by: NShuah Khan <skhan@linuxfoundation.org>
上级 44b7da5f
...@@ -168,42 +168,51 @@ class TestCounts: ...@@ -168,42 +168,51 @@ class TestCounts:
class LineStream: class LineStream:
""" """
A class to represent the lines of kernel output. A class to represent the lines of kernel output.
Provides a peek()/pop() interface over an iterator of Provides a lazy peek()/pop() interface over an iterator of
(line#, text). (line#, text).
""" """
_lines: Iterator[Tuple[int, str]] _lines: Iterator[Tuple[int, str]]
_next: Tuple[int, str] _next: Tuple[int, str]
_need_next: bool
_done: bool _done: bool
def __init__(self, lines: Iterator[Tuple[int, str]]): def __init__(self, lines: Iterator[Tuple[int, str]]):
"""Creates a new LineStream that wraps the given iterator.""" """Creates a new LineStream that wraps the given iterator."""
self._lines = lines self._lines = lines
self._done = False self._done = False
self._need_next = True
self._next = (0, '') self._next = (0, '')
self._get_next()
def _get_next(self) -> None: def _get_next(self) -> None:
"""Advances the LineSteam to the next line.""" """Advances the LineSteam to the next line, if necessary."""
if not self._need_next:
return
try: try:
self._next = next(self._lines) self._next = next(self._lines)
except StopIteration: except StopIteration:
self._done = True self._done = True
finally:
self._need_next = False
def peek(self) -> str: def peek(self) -> str:
"""Returns the current line, without advancing the LineStream. """Returns the current line, without advancing the LineStream.
""" """
self._get_next()
return self._next[1] return self._next[1]
def pop(self) -> str: def pop(self) -> str:
"""Returns the current line and advances the LineStream to """Returns the current line and advances the LineStream to
the next line. the next line.
""" """
n = self._next s = self.peek()
self._get_next() if self._done:
return n[1] raise ValueError(f'LineStream: going past EOF, last line was {s}')
self._need_next = True
return s
def __bool__(self) -> bool: def __bool__(self) -> bool:
"""Returns True if stream has more lines.""" """Returns True if stream has more lines."""
self._get_next()
return not self._done return not self._done
# Only used by kunit_tool_test.py. # Only used by kunit_tool_test.py.
...@@ -216,6 +225,7 @@ class LineStream: ...@@ -216,6 +225,7 @@ class LineStream:
def line_number(self) -> int: def line_number(self) -> int:
"""Returns the line number of the current line.""" """Returns the line number of the current line."""
self._get_next()
return self._next[0] return self._next[0]
# Parsing helper methods: # Parsing helper methods:
......
...@@ -13,9 +13,10 @@ import tempfile, shutil # Handling test_tmpdir ...@@ -13,9 +13,10 @@ import tempfile, shutil # Handling test_tmpdir
import itertools import itertools
import json import json
import os
import signal import signal
import subprocess import subprocess
import os from typing import Iterable
import kunit_config import kunit_config
import kunit_parser import kunit_parser
...@@ -331,6 +332,45 @@ class KUnitParserTest(unittest.TestCase): ...@@ -331,6 +332,45 @@ class KUnitParserTest(unittest.TestCase):
result.status) result.status)
self.assertEqual('kunit-resource-test', result.test.subtests[0].name) self.assertEqual('kunit-resource-test', result.test.subtests[0].name)
def line_stream_from_strs(strs: Iterable[str]) -> kunit_parser.LineStream:
return kunit_parser.LineStream(enumerate(strs, start=1))
class LineStreamTest(unittest.TestCase):
def test_basic(self):
stream = line_stream_from_strs(['hello', 'world'])
self.assertTrue(stream, msg='Should be more input')
self.assertEqual(stream.line_number(), 1)
self.assertEqual(stream.peek(), 'hello')
self.assertEqual(stream.pop(), 'hello')
self.assertTrue(stream, msg='Should be more input')
self.assertEqual(stream.line_number(), 2)
self.assertEqual(stream.peek(), 'world')
self.assertEqual(stream.pop(), 'world')
self.assertFalse(stream, msg='Should be no more input')
with self.assertRaisesRegex(ValueError, 'LineStream: going past EOF'):
stream.pop()
def test_is_lazy(self):
called_times = 0
def generator():
nonlocal called_times
for i in range(1,5):
called_times += 1
yield called_times, str(called_times)
stream = kunit_parser.LineStream(generator())
self.assertEqual(called_times, 0)
self.assertEqual(stream.pop(), '1')
self.assertEqual(called_times, 1)
self.assertEqual(stream.pop(), '2')
self.assertEqual(called_times, 2)
class LinuxSourceTreeTest(unittest.TestCase): class LinuxSourceTreeTest(unittest.TestCase):
def setUp(self): def setUp(self):
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册