gtk: Create an XunitList base class

The XunitList implements the Gio.ListModel interface, and builds in some
virtual functions to query and fill out the rows in the list in a
standard way. I also update the TestCaseList and SummaryList to both
inherit from this class.

Signed-off-by: Anna Schumaker <anna@nowheycreamery.com>
This commit is contained in:
Anna Schumaker 2023-08-15 13:56:27 -04:00
parent c45ec1909e
commit 5dc7735ba7
2 changed files with 112 additions and 103 deletions

View File

@ -58,6 +58,49 @@ class TestXunitRow(unittest.TestCase):
self.assertEqual(xunit.name, "xunit-2")
class TestXunitList(unittest.TestCase):
"""Test case for our base XunitList object."""
def setUp(self):
"""Set up common variables."""
self.xfstestsdb = xfstestsdb.Command()
with unittest.mock.patch("sys.stdout"):
self.xfstestsdb.run(["new", "/dev/vda1"])
self.xfstestsdb.run(["xunit", "read", "--name", "xunit-1",
"1", str(tests.xunit.XUNIT_1)])
self.xfstestsdb.run(["xunit", "read", "--name", "xunit-2",
"1", str(tests.xunit.XUNIT_1)])
self.xulist = xfstestsdb.gtk.model.XunitList(self.xfstestsdb.sql, 1)
def test_init(self):
"""Test that the XunitList was set up properly."""
self.assertIsInstance(self.xulist, GObject.GObject)
self.assertIsInstance(self.xulist, Gio.ListModel)
self.assertEqual(self.xulist.runid, 1)
def test_get_item_type(self):
"""Test the get_item_type() function."""
self.assertEqual(self.xulist.get_item_type(),
xfstestsdb.gtk.model.XunitRow.__gtype__)
def test_get_n_items(self):
"""Test the get_n_items() function."""
self.assertEqual(self.xulist.get_n_items(), 0)
self.assertEqual(self.xulist.n_items, 0)
self.xulist.n_items = 2
self.assertEqual(self.xulist.get_n_items(), 2)
def test_get_item(self):
"""Test the get_item() function."""
self.assertIsNone(self.xulist.get_item(0))
def test_get_xunits(self):
"""Test the get_xunits() function."""
self.assertListEqual(self.xulist.get_xunits(), ["xunit-1", "xunit-2"])
class TestTestResult(unittest.TestCase):
"""Tests a single TestCase Xunit instance."""
@ -133,18 +176,8 @@ class TestCaseList(unittest.TestCase):
def test_init(self):
"""Test that the TestCaseList was set up properly."""
self.assertIsInstance(self.tclist, GObject.GObject)
self.assertIsInstance(self.tclist, Gio.ListModel)
self.assertIsInstance(self.tclist, xfstestsdb.gtk.model.XunitList)
self.assertEqual(self.tclist.runid, 1)
def test_get_item_type(self):
"""Test the get_item_type() function."""
self.assertEqual(self.tclist.get_item_type(),
xfstestsdb.gtk.model.TestCase.__gtype__)
def test_get_n_items(self):
"""Test the get_n_items() function."""
self.assertEqual(self.tclist.get_n_items(), 10)
self.assertEqual(self.tclist.n_items, 10)
def test_get_item(self):
@ -163,10 +196,6 @@ class TestCaseList(unittest.TestCase):
self.assertIsNone(self.tclist.get_item(10))
def test_get_xunits(self):
"""Test the get_xunits() function."""
self.assertListEqual(self.tclist.get_xunits(), ["xunit-1", "xunit-2"])
class TestCaseFilter(unittest.TestCase):
"""Tests our Gtk.Filter customized for filtering TestCases."""
@ -314,18 +343,8 @@ class TestSummaryList(unittest.TestCase):
def test_init(self):
"""Test that the SummaryList was set up properly."""
self.assertIsInstance(self.summary, GObject.GObject)
self.assertIsInstance(self.summary, Gio.ListModel)
self.assertIsInstance(self.summary, xfstestsdb.gtk.model.XunitList)
self.assertEqual(self.summary.runid, 1)
def test_get_item_type(self):
"""Test the get_item_type() function."""
self.assertEqual(self.summary.get_item_type(),
xfstestsdb.gtk.model.Summary.__gtype__)
def test_get_n_items(self):
"""Test the get_n_items() function."""
self.assertEqual(self.summary.get_n_items(), 4)
self.assertEqual(self.summary.n_items, 4)
def test_get_item(self):
@ -342,7 +361,3 @@ class TestSummaryList(unittest.TestCase):
case "skipped": expected = {"3 testcases"}
case "time": expected = {"43 seconds"}
self.assertSetEqual(summary.get_results(), expected)
def test_get_xunits(self):
"""Test the get_xunits() function."""
self.assertListEqual(self.summary.get_xunits(), ["xunit-1", "xunit-2"])

View File

@ -1,5 +1,6 @@
# Copyright 2023 (c) Anna Schumaker.
"""Our Testcase Gio.ListModel."""
import sqlite3
import typing
from gi.repository import GObject
from gi.repository import Gio
@ -48,6 +49,50 @@ class XunitRow(GObject.GObject):
return {str(xunit) for xunit in self.__xunits.values()}
class XunitList(GObject.GObject, Gio.ListModel):
"""A list of XunitRows for a specific Xfstests Run."""
runid = GObject.Property(type=int)
n_items = GObject.Property(type=int)
def __init__(self, sql: sqlite.Connection, runid: int) -> None:
"""Initialize an XunitList."""
super().__init__(runid=runid)
self.__xunits = set()
rows = {}
for row in self.do_query(sql).fetchall():
self.do_parse(rows, row)
self.__xunits.add(row["xunit"])
self.__items = sorted(rows.values())
self.n_items = len(self.__items)
def do_get_item_type(self) -> GObject.GType:
"""Get the type of the objects in the list."""
return XunitRow.__gtype__
def do_get_n_items(self) -> int:
"""Get the number of items in the list."""
return self.n_items
def do_get_item(self, n: int) -> XunitRow | None:
"""Get a specific item on the list."""
return self.__items[n] if n < self.n_items else None
def do_parse(self, rows: dict[XunitRow], row: sqlite3.Row) -> None:
"""Parse a sqlite3.Row and add it to the rows dict."""
def do_query(self, sql: sqlite.Connection) -> sqlite3.Cursor:
"""Query the database."""
return sql("SELECT name AS xunit FROM xunits WHERE runid=?",
self.runid)
def get_xunits(self) -> list[str]:
"""Get a list of xunits attached to this xfstests run."""
return sorted(self.__xunits)
class TestResult(XunitCell):
"""The results for a single TestCase with a specific Xunit."""
@ -75,46 +120,20 @@ class TestCase(XunitRow):
stderr=("" if stderr is None else stderr))
class TestCaseList(GObject.GObject, Gio.ListModel):
class TestCaseList(XunitList):
"""A list of TestCases for a specific Xfstests Run."""
runid = GObject.Property(type=int)
n_items = GObject.Property(type=int)
def do_query(self, sql: sqlite.Connection) -> sqlite3.Cursor:
"""Query the database for testcase results."""
return sql("""SELECT testcase, xunit, status, time,
message, stdout, stderr
FROM testcases_view WHERE runid=?""", self.runid)
def __init__(self, sql: sqlite.Connection, runid: int) -> None:
"""Initialize a TestCaseList."""
super().__init__(runid=runid)
self.__xunits = set()
cases = {}
cur = sql("""SELECT testcase, xunit, status,
time, message, stdout, stderr
FROM testcases_view WHERE runid=?""", runid)
for row in cur.fetchall():
testcase = cases.setdefault(row["testcase"],
TestCase(row["testcase"]))
testcase.add_xunit(row["xunit"], row["status"], row["time"],
row["message"], row["stdout"], row["stderr"])
self.__xunits.add(row["xunit"])
self.__items = sorted(cases.values())
self.n_items = len(self.__items)
def do_get_item_type(self) -> GObject.GType:
"""Get the type of the objects in the list."""
return TestCase.__gtype__
def do_get_n_items(self) -> int:
"""Get the number of items in the list."""
return self.n_items
def do_get_item(self, n: int) -> TestCase | None:
"""Get a specific item on the list."""
return self.__items[n] if n < self.n_items else None
def get_xunits(self) -> list[str]:
"""Get a list of xunits attached to this xfstests run."""
return sorted(self.__xunits)
def do_parse(self, rows: dict[TestCase], row: sqlite3.Cursor) -> None:
"""Parse the data in the row and add it to the rows dict."""
testcase = rows.setdefault(row["testcase"], TestCase(row["testcase"]))
testcase.add_xunit(row["xunit"], row["status"], row["time"],
row["message"], row["stdout"], row["stderr"])
class TestCaseFilter(Gtk.Filter):
@ -183,42 +202,17 @@ class Summary(XunitRow):
return SummaryValue(name=name, value=value, unit=unit)
class SummaryList(GObject.GObject, Gio.ListModel):
class SummaryList(XunitList):
"""A list summarizing the results of a specific Xfstests Run."""
runid = GObject.Property(type=int)
n_items = GObject.Property(type=int)
def do_query(self, sql: sqlite.Connection) -> sqlite3.Cursor:
"""Query the database for xunit summaries."""
return sql("""SELECT name AS xunit, passed, failed, skipped, time
FROM xunits_view WHERE runid=?""", self.runid)
def __init__(self, sql: sqlite.Connection, runid: int) -> None:
"""Initialize a SummaryList."""
super().__init__(runid=runid)
self.__xunits = set()
results = {}
cur = sql("""SELECT name AS xunit, passed, failed, skipped, time
FROM xunits_view WHERE runid=?""", runid)
for row in cur.fetchall():
for field in ["passed", "failed", "skipped", "time"]:
summary = results.setdefault(field, Summary(field))
summary.add_xunit(row["xunit"], row[field],
"second" if field == "time" else "testcase")
self.__xunits.add(row["xunit"])
self.__items = sorted(results.values())
self.n_items = len(self.__items)
def do_get_item_type(self) -> GObject.GType:
"""Get the type of the objects in the list."""
return Summary.__gtype__
def do_get_n_items(self) -> int:
"""Get the number of items in the list."""
return self.n_items
def do_get_item(self, n: int) -> Summary | None:
"""Get a specific item on the list."""
return self.__items[n] if n < self.n_items else None
def get_xunits(self) -> list[str]:
"""Get a list of xunits attached to this xfstests run."""
return sorted(self.__xunits)
def do_parse(self, rows: dict[Summary], row: sqlite3.Row) -> None:
"""Parse the data in the row and add it to the rows dict."""
for field in ["passed", "failed", "skipped", "time"]:
summary = rows.setdefault(field, Summary(field))
summary.add_xunit(row["xunit"], row[field],
"second" if field == "time" else "testcase")