xfstestsdb: Create the `xfstestsdb xunit list` command
This command prints out information about the known xunit files. It has extra options for filtering by runid, device, name, and hostname. I also gave it optional Timestamp and Results columns that the user can enable. Implements: #12 (`xfstestsdb xunit list`) Signed-off-by: Anna Schumaker <anna@nowheycreamery.com>
This commit is contained in:
parent
f4b77f527b
commit
e27bc9f5ae
|
@ -0,0 +1,191 @@
|
|||
# Copyright 2023 (c) Anna Schumaker.
|
||||
"""Tests the `xfstestsdb xunit list` command."""
|
||||
import datetime
|
||||
import dateutil.tz
|
||||
import io
|
||||
import unittest
|
||||
import unittest.mock
|
||||
import xfstestsdb.xunit.list
|
||||
import tests.xunit
|
||||
|
||||
|
||||
class TestXunitList(unittest.TestCase):
|
||||
"""Tests the `xfstestsdb xunit list` command."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up common variables."""
|
||||
self.xfstestsdb = xfstestsdb.Command()
|
||||
self.xunit = self.xfstestsdb.commands["xunit"]
|
||||
self.list = self.xunit.commands["list"]
|
||||
|
||||
def setup_runs(self, mock_stdout: io.StringIO):
|
||||
"""Set up runs in the database and clear stdout."""
|
||||
timestamp = datetime.datetime(2023, 1, 29, 14, 14, 14)
|
||||
timestamp = timestamp.astimezone(tz=dateutil.tz.tzutc())
|
||||
timestamp = timestamp.replace(tzinfo=None)
|
||||
|
||||
self.xfstestsdb.run(["new", "/dev/vda1"])
|
||||
self.xfstestsdb.run(["new", "/dev/vda2"])
|
||||
self.xfstestsdb.run(["new", "/dev/vdb1"])
|
||||
|
||||
self.xfstestsdb.run(["xunit", "read", "1", str(tests.xunit.XUNIT_1)])
|
||||
self.xfstestsdb.run(["xunit", "read", "1", str(tests.xunit.XUNIT_1),
|
||||
"--name", "test-2"])
|
||||
self.xfstestsdb.run(["xunit", "read", "3", str(tests.xunit.XUNIT_1)])
|
||||
|
||||
self.xfstestsdb.sql.executemany("UPDATE xunits SET timestamp=? "
|
||||
"WHERE rowid=?", (timestamp, 1),
|
||||
(timestamp.replace(day=30), 2),
|
||||
(timestamp.replace(day=31), 3))
|
||||
self.xfstestsdb.sql("UPDATE xunits SET hostname=? WHERE rowid=?",
|
||||
"myhost2", 3)
|
||||
|
||||
mock_stdout.seek(0)
|
||||
mock_stdout.truncate(0)
|
||||
|
||||
def test_init(self):
|
||||
"""Check that the xunit list command was set up properly."""
|
||||
self.assertIsInstance(self.list, xfstestsdb.commands.Command)
|
||||
self.assertIsInstance(self.list, xfstestsdb.xunit.list.Command)
|
||||
self.assertEqual(self.xunit.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(["xunit", "list", "--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(), "")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_xunits(self, mock_stdout: io.StringIO):
|
||||
"""Test listing xunits with default options."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["xunit", "list", "--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+
|
||||
| run | device | xunit | hostname |
|
||||
+-----+-----------+--------+----------+
|
||||
| 1 | /dev/vda1 | test-1 | myhost |
|
||||
| 1 | /dev/vda1 | test-2 | myhost |
|
||||
| 3 | /dev/vdb1 | test-1 | myhost2 |
|
||||
+-----+-----------+--------+----------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_filter_run(self, mock_stdout: io.StringIO):
|
||||
"""Test listing xunits beloging to a specific run."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["xunit", "list", "--runid", "1",
|
||||
"--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+
|
||||
| run | device | xunit | hostname |
|
||||
+-----+-----------+--------+----------+
|
||||
| 1 | /dev/vda1 | test-1 | myhost |
|
||||
| 1 | /dev/vda1 | test-2 | myhost |
|
||||
+-----+-----------+--------+----------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_filter_device(self, mock_stdout: io.StringIO):
|
||||
"""Test listing xunits matching a device pattern."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["xunit", "list", "--device", "*vdb*",
|
||||
"--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+
|
||||
| run | device | xunit | hostname |
|
||||
+-----+-----------+--------+----------+
|
||||
| 3 | /dev/vdb1 | test-1 | myhost2 |
|
||||
+-----+-----------+--------+----------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_filter_xunit(self, mock_stdout: io.StringIO):
|
||||
"""Test listing xunits matching a name pattern."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["xunit", "list", "--xunit", "test*1",
|
||||
"--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+
|
||||
| run | device | xunit | hostname |
|
||||
+-----+-----------+--------+----------+
|
||||
| 1 | /dev/vda1 | test-1 | myhost |
|
||||
| 3 | /dev/vdb1 | test-1 | myhost2 |
|
||||
+-----+-----------+--------+----------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_filter_hostname(self, mock_stdout: io.StringIO):
|
||||
"""Test listing runs with a specific timestamp pattern."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["xunit", "list", "--hostname", "my*2",
|
||||
"--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+
|
||||
| run | device | xunit | hostname |
|
||||
+-----+-----------+--------+----------+
|
||||
| 3 | /dev/vdb1 | test-1 | myhost2 |
|
||||
+-----+-----------+--------+----------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_xunit_timestamp(self, mock_stdout: io.StringIO):
|
||||
"""Test listing runs with the timestamp column added."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["xunit", "list", "--timestamp",
|
||||
"--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+---------------------+
|
||||
| run | device | xunit | hostname | timestamp |
|
||||
+-----+-----------+--------+----------+---------------------+
|
||||
| 1 | /dev/vda1 | test-1 | myhost | 2023-01-29 14:14:14 |
|
||||
| 1 | /dev/vda1 | test-2 | myhost | 2023-01-30 14:14:14 |
|
||||
| 3 | /dev/vdb1 | test-1 | myhost2 | 2023-01-31 14:14:14 |
|
||||
+-----+-----------+--------+----------+---------------------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_filter_timestamp(self, mock_stdout: io.StringIO):
|
||||
"""Test listing runs with a specific timestamp pattern."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["xunit", "list", "--timestamp", "*31 14:*",
|
||||
"--color", "none"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+---------------------+
|
||||
| run | device | xunit | hostname | timestamp |
|
||||
+-----+-----------+--------+----------+---------------------+
|
||||
| 3 | /dev/vdb1 | test-1 | myhost2 | 2023-01-31 14:14:14 |
|
||||
+-----+-----------+--------+----------+---------------------+
|
||||
""")
|
||||
|
||||
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||
def test_list_xunit_results(self, mock_stdout: io.StringIO):
|
||||
"""Test listing xunits with the results columns added."""
|
||||
self.setup_runs(mock_stdout)
|
||||
print()
|
||||
self.xfstestsdb.run(["xunit", "list", "--color", "none", "--results"])
|
||||
self.assertEqual(mock_stdout.getvalue(),
|
||||
"""
|
||||
+-----+-----------+--------+----------+------+------+------+------+
|
||||
| run | device | xunit | hostname | pass | fail | skip | time |
|
||||
+-----+-----------+--------+----------+------+------+------+------+
|
||||
| 1 | /dev/vda1 | test-1 | myhost | 6 | 1 | 3 | 43 s |
|
||||
| 1 | /dev/vda1 | test-2 | myhost | 6 | 1 | 3 | 43 s |
|
||||
| 3 | /dev/vdb1 | test-1 | myhost2 | 6 | 1 | 3 | 43 s |
|
||||
+-----+-----------+--------+----------+------+------+------+------+
|
||||
""")
|
|
@ -64,7 +64,8 @@ CREATE TABLE xunits (
|
|||
);
|
||||
|
||||
CREATE VIEW xunits_view AS
|
||||
SELECT runid, name, hostname, tests, failed, skipped, time,
|
||||
SELECT runid, device, name, hostname, tests, failed, skipped, time,
|
||||
(tests - (skipped + failed)) as passed,
|
||||
datetime(timestamp, 'localtime') as timestamp
|
||||
FROM xunits;
|
||||
datetime(xunits.timestamp, 'localtime') as timestamp
|
||||
FROM xunits
|
||||
JOIN xfstests_runs USING (runid);
|
||||
|
|
|
@ -4,6 +4,7 @@ import argparse
|
|||
from .. import commands
|
||||
from .. import sqlite
|
||||
from . import delete
|
||||
from . import list
|
||||
from . import read
|
||||
from . import rename
|
||||
|
||||
|
@ -18,6 +19,7 @@ class Command(commands.Command):
|
|||
help="xfstestsdb xunit commands")
|
||||
self.subparser = self.parser.add_subparsers(title="xunit commands")
|
||||
self.commands = {"delete": delete.Command(self.subparser, sql),
|
||||
"list": list.Command(self.subparser, sql),
|
||||
"read": read.Command(self.subparser, sql),
|
||||
"rename": rename.Command(self.subparser, sql)}
|
||||
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
# Copyright 2023 (c) Anna Schumaker.
|
||||
"""The `xfstestsdb xunit list` command."""
|
||||
import argparse
|
||||
from .. import colors
|
||||
from .. import commands
|
||||
from .. import sqlite
|
||||
from .. import table
|
||||
|
||||
|
||||
class Command(commands.Command):
|
||||
"""The `xfstestsdb xunit list` command."""
|
||||
|
||||
def __init__(self, subparser: argparse.Action,
|
||||
sql: sqlite.Connection) -> None:
|
||||
"""Set up the list command."""
|
||||
super().__init__(subparser, sql, "list",
|
||||
help="list the xfstest xunit entries")
|
||||
self.parser.add_argument("--color", metavar="color", nargs="?",
|
||||
choices=["light", "dark", "none"],
|
||||
const=colors.get_default_colors(),
|
||||
default=colors.get_default_colors(),
|
||||
help="show xunits with color output "
|
||||
f"[default={colors.get_default_colors()}]")
|
||||
self.parser.add_argument("--device", metavar="device", nargs=1,
|
||||
help="show xunits with a device matching "
|
||||
"the given pattern")
|
||||
self.parser.add_argument("--hostname", metavar="hostname", nargs=1,
|
||||
help="show xunits with a hostname matching "
|
||||
"the given pattern")
|
||||
self.parser.add_argument("--results", action="store_true",
|
||||
help="show the results columns")
|
||||
self.parser.add_argument("--runid", metavar="runid", nargs=1, type=int,
|
||||
help="show xunits belonging to the given run")
|
||||
self.parser.add_argument("--timestamp", metavar="timestamp", nargs="?",
|
||||
const=True, default=False,
|
||||
help="show xunits with a timestamp matching "
|
||||
"the given pattern")
|
||||
self.parser.add_argument("--xunit", metavar="xunit", nargs=1,
|
||||
help="show xunits with a name "
|
||||
"matching the given pattern")
|
||||
|
||||
def do_command(self, args: argparse.Namespace) -> None:
|
||||
"""Print out the xfstestsdb xunits table."""
|
||||
tbl = table.Table(["run", "device", "xunit", "hostname"],
|
||||
["r", "l", "l", "l"], args.color)
|
||||
sql_where = ""
|
||||
sql_args = []
|
||||
filter = []
|
||||
|
||||
if args.device:
|
||||
filter.append("device GLOB ?")
|
||||
sql_args.append(args.device[0])
|
||||
|
||||
if args.hostname:
|
||||
filter.append("hostname GLOB ?")
|
||||
sql_args.append(args.hostname[0])
|
||||
|
||||
if args.timestamp:
|
||||
tbl.add_column("timestamp", "c")
|
||||
if isinstance(args.timestamp, str):
|
||||
filter.append("timestamp GLOB ?")
|
||||
sql_args.append(args.timestamp)
|
||||
|
||||
if args.results:
|
||||
tbl.add_column("pass", "r")
|
||||
tbl.add_column("fail", "r")
|
||||
tbl.add_column("skip", "r")
|
||||
tbl.add_column("time", "r")
|
||||
|
||||
if args.runid:
|
||||
filter.append("runid GLOB ?")
|
||||
sql_args.append(args.runid[0])
|
||||
|
||||
if args.xunit:
|
||||
filter.append("name GLOB ?")
|
||||
sql_args.append(args.xunit[0])
|
||||
|
||||
if len(filter) > 0:
|
||||
sql_where = " WHERE " + " AND ".join(filter) + " "
|
||||
|
||||
cur = self.sql("""SELECT runid, device, name, hostname, timestamp,
|
||||
tests, passed, failed, skipped, time
|
||||
FROM xunits_view""" + sql_where + """
|
||||
ORDER BY runid, name""", *sql_args)
|
||||
for row in cur.fetchall():
|
||||
data = [row["runid"], row["device"], row["name"], row["hostname"]]
|
||||
if args.timestamp:
|
||||
data.append(row["timestamp"])
|
||||
if args.results:
|
||||
data.extend([row["passed"], row["failed"], row["skipped"],
|
||||
f"{row['time']} s"])
|
||||
tbl.add_row(*data)
|
||||
|
||||
if len(tbl.rows) > 0:
|
||||
print(tbl)
|
Loading…
Reference in New Issue