testcase: Create the xfstestsdb testcase list
command
This command prints out information about the known testcases. It has extra options to filter by runid, device, xunit, testcase, and status. Implements: #11 (`xfstestsdb testcase list`) Signed-off-by: Anna Schumaker <anna@nowheycreamery.com>
This commit is contained in:
parent
68a4b58a5a
commit
489c08e55c
266
tests/testcase/test_list.py
Normal file
266
tests/testcase/test_list.py
Normal file
|
@ -0,0 +1,266 @@
|
|||
# Copyright 2023 (c) Anna Schumaker.
|
||||
"""Tests the `xfstestsdb testcase list` command."""
|
||||
import io
|
||||
import unittest
|
||||
import unittest.mock
|
||||
import xfstestsdb.testcase.list
|
||||
import tests.xunit
|
||||
|
||||
|
||||
class TestTestCaseList(unittest.TestCase):
|
||||
"""Tests the `xfstestsdb testcase list` command."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up common variables."""
|
||||
self.xfstestsdb = xfstestsdb.Command()
|
||||
self.testcase = self.xfstestsdb.commands["testcase"]
|
||||
self.list = self.testcase.commands["list"]
|
||||
|
||||
def setup_runs(self, mock_stdout: io.StringIO):
|
||||
"""Set up runs in the database and clear stdout."""
|
||||
self.xfstestsdb.run(["new", "/dev/vda1"])
|
||||
self.xfstestsdb.run(["new", "/dev/vdb2"])
|
||||
|
||||
self.xfstestsdb.run(["xunit", "read", "1", str(tests.xunit.XUNIT_1)])
|
||||
self.xfstestsdb.run(["xunit", "read", "2", str(tests.xunit.XUNIT_1),
|
||||
"--name", "test-2"])
|
||||
|
||||
mock_stdout.seek(0)
|
||||
mock_stdout.truncate(0)
|
||||
|
||||
def test_init(self):
|
||||
"""Check that the testcase list command was set up properly."""
|
||||
self.assertIsInstance(xfstestsdb.testcase.list.TestCaseTable(["col"]),
|
||||
xfstestsdb.table.Table)
|
||||
self.assertIsInstance(self.list, xfstestsdb.commands.Command)
|
||||
self.assertIsInstance(self.list, xfstestsdb.testcase.list.Command)
|
||||
self.assertEqual(self.testcase.subparser.choices["list"],
|
||||
self.list.parser)
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_empty(self, mock_stdout: io.StringIO):
|
||||
"""Test printing out an empty list."""
|
||||
self.xfstestsdb.run(["testcase", "list", "--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(), "")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_testcases(self, mock_stdout: io.StringIO):
|
||||
"""Test listing xunits with default options."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["testcase", "list", "--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
| run | device | xunit | testcase | status | time |
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
| 1 | /dev/vda1 | test-1 | test/01 | passed | 1 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/02 | skipped | 0 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/03 | skipped | 0 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/04 | passed | 4 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/05 | passed | 5 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/06 | passed | 6 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/07 | skipped | 0 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/08 | passed | 8 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/09 | failure | 9 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/10 | passed | 10 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/01 | passed | 1 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/02 | skipped | 0 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/03 | skipped | 0 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/04 | passed | 4 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/05 | passed | 5 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/06 | passed | 6 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/07 | skipped | 0 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/08 | passed | 8 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/09 | failure | 9 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/10 | passed | 10 s |
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_filter_run(self, mock_stdout: io.StringIO):
|
||||
"""Test listing testcases beloging to a specific run."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["testcase", "list", "--runid", "1",
|
||||
"--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
| run | device | xunit | testcase | status | time |
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
| 1 | /dev/vda1 | test-1 | test/01 | passed | 1 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/02 | skipped | 0 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/03 | skipped | 0 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/04 | passed | 4 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/05 | passed | 5 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/06 | passed | 6 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/07 | skipped | 0 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/08 | passed | 8 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/09 | failure | 9 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/10 | passed | 10 s |
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_filter_device(self, mock_stdout: io.StringIO):
|
||||
"""Test listing testcases matching a device pattern."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["testcase", "list", "--device", "*vdb*",
|
||||
"--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
| run | device | xunit | testcase | status | time |
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
| 2 | /dev/vdb2 | test-2 | test/01 | passed | 1 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/02 | skipped | 0 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/03 | skipped | 0 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/04 | passed | 4 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/05 | passed | 5 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/06 | passed | 6 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/07 | skipped | 0 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/08 | passed | 8 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/09 | failure | 9 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/10 | passed | 10 s |
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_filter_xunit(self, mock_stdout: io.StringIO):
|
||||
"""Test listing testcases matching an xunit pattern."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["testcase", "list", "--xunit", "*1",
|
||||
"--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
| run | device | xunit | testcase | status | time |
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
| 1 | /dev/vda1 | test-1 | test/01 | passed | 1 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/02 | skipped | 0 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/03 | skipped | 0 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/04 | passed | 4 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/05 | passed | 5 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/06 | passed | 6 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/07 | skipped | 0 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/08 | passed | 8 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/09 | failure | 9 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/10 | passed | 10 s |
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_filter_testcase(self, mock_stdout: io.StringIO):
|
||||
"""Test listing testcases matching a specific pattern."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["testcase", "list", "--testcase", "test*1*",
|
||||
"--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+--------+------+
|
||||
| run | device | xunit | testcase | status | time |
|
||||
+-----+-----------+--------+----------+--------+------+
|
||||
| 1 | /dev/vda1 | test-1 | test/01 | passed | 1 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/10 | passed | 10 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/01 | passed | 1 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/10 | passed | 10 s |
|
||||
+-----+-----------+--------+----------+--------+------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_filter_passed(self, mock_stdout: io.StringIO):
|
||||
"""Test listing testcases that passed."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["testcase", "list", "--passed",
|
||||
"--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+--------+------+
|
||||
| run | device | xunit | testcase | status | time |
|
||||
+-----+-----------+--------+----------+--------+------+
|
||||
| 1 | /dev/vda1 | test-1 | test/01 | passed | 1 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/04 | passed | 4 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/05 | passed | 5 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/06 | passed | 6 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/08 | passed | 8 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/10 | passed | 10 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/01 | passed | 1 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/04 | passed | 4 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/05 | passed | 5 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/06 | passed | 6 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/08 | passed | 8 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/10 | passed | 10 s |
|
||||
+-----+-----------+--------+----------+--------+------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_filter_skipped(self, mock_stdout: io.StringIO):
|
||||
"""Test listing testcases that were skipped."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["testcase", "list", "--skipped",
|
||||
"--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
| run | device | xunit | testcase | status | time |
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
| 1 | /dev/vda1 | test-1 | test/02 | skipped | 0 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/03 | skipped | 0 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/07 | skipped | 0 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/02 | skipped | 0 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/03 | skipped | 0 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/07 | skipped | 0 s |
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_filter_failure(self, mock_stdout: io.StringIO):
|
||||
"""Test listing testcases that failed."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["testcase", "list", "--failure",
|
||||
"--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
| run | device | xunit | testcase | status | time |
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
| 1 | /dev/vda1 | test-1 | test/09 | failure | 9 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/09 | failure | 9 s |
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_filter_pass_fail(self, mock_stdout: io.StringIO):
|
||||
"""Test listing testcases that passed and failed."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["testcase", "list", "--passed", "--failure",
|
||||
"--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
| run | device | xunit | testcase | status | time |
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
| 1 | /dev/vda1 | test-1 | test/01 | passed | 1 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/04 | passed | 4 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/05 | passed | 5 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/06 | passed | 6 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/08 | passed | 8 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/09 | failure | 9 s |
|
||||
| 1 | /dev/vda1 | test-1 | test/10 | passed | 10 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/01 | passed | 1 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/04 | passed | 4 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/05 | passed | 5 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/06 | passed | 6 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/08 | passed | 8 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/09 | failure | 9 s |
|
||||
| 2 | /dev/vdb2 | test-2 | test/10 | passed | 10 s |
|
||||
+-----+-----------+--------+----------+---------+------+
|
||||
""")
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright 2023 (c) Anna Schumaker.
|
||||
"""Tests the `xfstestsdb testcase` command."""
|
||||
import argparse
|
||||
import io
|
||||
import unittest
|
||||
import unittest.mock
|
||||
|
@ -18,6 +19,7 @@ class TestTestcase(unittest.TestCase):
|
|||
"""Check that the testcase command was set up properly."""
|
||||
self.assertIsInstance(self.testcase, xfstestsdb.commands.Command)
|
||||
self.assertIsInstance(self.testcase, xfstestsdb.testcase.Command)
|
||||
self.assertIsInstance(self.testcase.subparser, argparse.Action)
|
||||
self.assertEqual(self.xfstestsdb.subparser.choices["testcase"],
|
||||
self.testcase.parser)
|
||||
|
||||
|
@ -26,4 +28,4 @@ class TestTestcase(unittest.TestCase):
|
|||
"""Test calling `xfstestdb xunit` with an empty argument list."""
|
||||
self.xfstestsdb.run(["testcase"])
|
||||
self.assertRegex(mock_stdout.getvalue(),
|
||||
r"^usage: .*? testcase \[\-h\]$")
|
||||
r"^usage: .*? testcase \[\-h\] \{.*?} ...$")
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import argparse
|
||||
from .. import commands
|
||||
from .. import sqlite
|
||||
from . import list
|
||||
|
||||
|
||||
class Command(commands.Command):
|
||||
|
@ -13,6 +14,8 @@ class Command(commands.Command):
|
|||
"""Set up the testcase command."""
|
||||
super().__init__(subparser, sql, "testcase",
|
||||
help="xfstestsdb testcase commands")
|
||||
self.subparser = self.parser.add_subparsers(title="testcase commands")
|
||||
self.commands = {"list": list.Command(self.subparser, sql)}
|
||||
|
||||
def do_command(self, args: argparse.Namespace) -> None:
|
||||
"""Print help text for the testcase subcommand."""
|
||||
|
|
97
xfstestsdb/testcase/list.py
Normal file
97
xfstestsdb/testcase/list.py
Normal file
|
@ -0,0 +1,97 @@
|
|||
# Copyright 2023 (c) Anna Schumaker.
|
||||
"""The `xfstestsdb testcase list` command."""
|
||||
import argparse
|
||||
from .. import colors
|
||||
from .. import commands
|
||||
from .. import sqlite
|
||||
from .. import table
|
||||
|
||||
|
||||
class TestCaseTable(table.Table):
|
||||
"""A custom Table for coloring rows based on status."""
|
||||
|
||||
def do_format_cell(self, rownum: int, text: str, color: str,
|
||||
**kwargs) -> str:
|
||||
"""Set row text color based on testcase status."""
|
||||
color = f"fg-{self.rows[rownum][-2]}"
|
||||
return super().do_format_cell(rownum, text, color, **kwargs)
|
||||
|
||||
|
||||
class Command(commands.Command):
|
||||
"""The `xfstestsdb testcase list` command."""
|
||||
|
||||
def __init__(self, subparser: argparse.Action,
|
||||
sql: sqlite.Connection) -> None:
|
||||
"""Set up the testcase list command."""
|
||||
super().__init__(subparser, sql, "list",
|
||||
help="list the xfstest testcase entries")
|
||||
self.parser.add_argument("--color", metavar="color", nargs="?",
|
||||
choices=["light", "dark", "none"],
|
||||
const=colors.get_default_colors(),
|
||||
default=colors.get_default_colors(),
|
||||
help="list testcases with color output "
|
||||
f"[default={colors.get_default_colors()}]")
|
||||
self.parser.add_argument("--device", metavar="device", nargs=1,
|
||||
help="show testcases with a device matching "
|
||||
"the given pattern")
|
||||
self.parser.add_argument("--failure", dest="status",
|
||||
action="append_const", const="failure",
|
||||
help="show failed testcases")
|
||||
self.parser.add_argument("--passed", dest="status",
|
||||
action="append_const", const="passed",
|
||||
help="show passing testcases")
|
||||
self.parser.add_argument("--runid", metavar="runid", nargs=1, type=int,
|
||||
help="show testcases belonging to "
|
||||
"a specific run")
|
||||
self.parser.add_argument("--skipped", dest="status",
|
||||
action="append_const", const="skipped",
|
||||
help="show skipped testcases")
|
||||
self.parser.add_argument("--testcase", metavar="testcase", nargs=1,
|
||||
help="show testcases matching "
|
||||
"the given pattern")
|
||||
self.parser.add_argument("--xunit", metavar="xunit", nargs=1,
|
||||
help="show testcases with an xunit "
|
||||
"matching the given pattern")
|
||||
|
||||
def do_command(self, args: argparse.Namespace) -> None:
|
||||
"""Print out the xfstestsdb xunits table."""
|
||||
tbl = TestCaseTable(["run", "device", "xunit",
|
||||
"testcase", "status", "time"],
|
||||
["r", "l", "l", "l", "l", "r"], args.color)
|
||||
sql_where = ""
|
||||
sql_args = []
|
||||
filter = []
|
||||
|
||||
if args.device:
|
||||
filter.append("device GLOB ?")
|
||||
sql_args.append(args.device[0])
|
||||
|
||||
if args.runid:
|
||||
filter.append("runid=?")
|
||||
sql_args.append(args.runid[0])
|
||||
|
||||
if args.status:
|
||||
parameters = ["?"] * len(args.status)
|
||||
filter.append(f"status IN ({','.join(parameters)})")
|
||||
sql_args.extend(args.status)
|
||||
|
||||
if args.testcase:
|
||||
filter.append("testcase GLOB ?")
|
||||
sql_args.append(args.testcase[0])
|
||||
|
||||
if args.xunit:
|
||||
filter.append("xunit GLOB ?")
|
||||
sql_args.append(args.xunit[0])
|
||||
|
||||
if len(filter) > 0:
|
||||
sql_where = " WHERE " + " AND ".join(filter) + " "
|
||||
|
||||
cur = self.sql("""SELECT runid, device, xunit, testcase, status, time
|
||||
FROM testcases_view""" + sql_where + """
|
||||
ORDER BY runid, xunit, testcase""", *sql_args)
|
||||
for row in cur.fetchall():
|
||||
tbl.add_row(row["runid"], row["device"], row["xunit"],
|
||||
row["testcase"], row["status"], f"{row['time']} s")
|
||||
|
||||
if len(tbl.rows) > 0:
|
||||
print(tbl)
|
Loading…
Reference in New Issue
Block a user