diff --git a/tests/gtk/test_tree.py b/tests/gtk/test_tree.py index 2ce86f0..4aa2cb0 100644 --- a/tests/gtk/test_tree.py +++ b/tests/gtk/test_tree.py @@ -3,8 +3,10 @@ import datetime import unittest import xfstestsdb.gtk.tree +import tests.xunit from gi.repository import GObject from gi.repository import Gio +from gi.repository import Gtk class TestXfstestsRun(unittest.TestCase): @@ -118,3 +120,76 @@ class TestDeviceRunModel(unittest.TestCase): then = now - datetime.timedelta(seconds=42) self.device.add_run(2, then) self.assertEqual(self.device.get_earliest_run().runid, 2) + + +class TestDateDeviceList(unittest.TestCase): + """Test case for our listmodel of test devices on a specific day.""" + + def setUp(self): + """Set up common variables.""" + self.xfstestsdb = xfstestsdb.Command() + with unittest.mock.patch("sys.stdout"): + self.xfstestsdb.run(["new", "/dev/vda2"]) + self.xfstestsdb.run(["new", "/dev/vda1"]) + self.xfstestsdb.run(["new", "/dev/vda1"]) + self.xfstestsdb.run(["new", "/dev/vda3"]) + + 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)]) + + yesterday = datetime.datetime.now() - datetime.timedelta(days=1) + self.xfstestsdb.sql("""UPDATE xfstests_runs SET timestamp=? + WHERE device=?""", yesterday, "/dev/vda3") + + self.today = datetime.date.today() + self.devlist = xfstestsdb.gtk.tree.DateDeviceList(self.xfstestsdb.sql, + self.today) + + def test_init(self): + """Test that the DateDeviceList was set up properly.""" + self.assertIsInstance(self.devlist, GObject.GObject) + self.assertIsInstance(self.devlist, Gio.ListModel) + self.assertEqual(self.devlist.date, datetime.date.today()) + + def test_get_range(self): + """Test finding the date range for the displayed entries.""" + min = datetime.datetime.combine(self.today, datetime.time()) + max = min + datetime.timedelta(days=1) + self.assertTupleEqual(self.devlist.get_range(self.today), (min, max)) + + def test_get_item_type(self): + """Test the get_item_type() function.""" + self.assertEqual(self.devlist.get_item_type(), + xfstestsdb.gtk.tree.DeviceRunsList.__gtype__) + + def test_get_n_items(self): + """Test the get_n_items() function.""" + self.assertEqual(self.devlist.get_n_items(), 2) + self.assertEqual(self.devlist.n_items, 2) + + def test_get_item(self): + """Test the get_item() function.""" + self.assertIsInstance(self.devlist.get_item(0), + xfstestsdb.gtk.tree.DeviceRunsList) + self.assertIsInstance(self.devlist.get_item(0).get_item(0).timestamp, + datetime.datetime) + self.assertEqual(self.devlist.get_item(0).name, "/dev/vda1") + self.assertEqual(self.devlist.get_item(1).name, "/dev/vda2") + + def test_treemodel(self): + """Test the treemodel property.""" + self.assertIsInstance(self.devlist.treemodel, Gtk.TreeListModel) + self.assertFalse(self.devlist.treemodel.props.passthrough) + self.assertTrue(self.devlist.treemodel.props.autoexpand) + + tree = self.devlist.treemodel + self.assertEqual(tree[0].get_item().name, "/dev/vda1") + self.assertEqual(tree[1].get_item().runid, 2) + self.assertEqual(tree[2].get_item().runid, 3) + self.assertEqual(tree[3].get_item().name, "/dev/vda2") + self.assertEqual(tree[4].get_item().runid, 1) + + with self.assertRaises(IndexError): + self.assertIsNone(tree[5]) diff --git a/xfstestsdb/gtk/tree.py b/xfstestsdb/gtk/tree.py index 5255ae7..07f3e7e 100644 --- a/xfstestsdb/gtk/tree.py +++ b/xfstestsdb/gtk/tree.py @@ -5,6 +5,8 @@ import datetime import typing from gi.repository import GObject from gi.repository import Gio +from gi.repository import Gtk +from .. import sqlite class XfstestsRun(GObject.GObject): @@ -72,3 +74,55 @@ class DeviceRunsList(GObject.GObject, Gio.ListModel): def get_earliest_run(self) -> XfstestsRun | None: """Get the earliest XfstestsRun that we know about.""" return self.__runs[0] if self.n_items > 0 else None + + +class DateDeviceList(GObject.GObject, Gio.ListModel): + """A list containing test devices used on a specific day.""" + + date = GObject.Property(type=GObject.TYPE_PYOBJECT) + n_items = GObject.Property(type=int) + treemodel = GObject.Property(type=Gtk.TreeListModel) + + def __init__(self, sql: sqlite.Connection, date: datetime.date) -> None: + """Initialize our DateDeviceList.""" + super().__init__(date=date) + + devices = {} + for row in sql("""SELECT DISTINCT runid, device, timestamp + FROM tagged_runs + WHERE timestamp >= ? AND timestamp < ?""", + *self.get_range(date)).fetchall(): + if (dev := devices.get(row['device'])) is None: + devices[row['device']] = dev = DeviceRunsList(row['device']) + ts = datetime.datetime.fromisoformat(row['timestamp']) + dev.add_run(row['runid'], ts) + + self.__items = sorted(devices.values()) + self.n_items = len(self.__items) + self.treemodel = Gtk.TreeListModel.new(root=self, passthrough=False, + autoexpand=True, + create_func=self.__create_func) + + def __create_func(self, item: GObject.GObject) -> Gio.ListModel: + if isinstance(item, DeviceRunsList): + return item + return None + + def do_get_item_type(self) -> GObject.GType: + """Get the type of objects in the list.""" + return DeviceRunsList.__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) -> DeviceRunsList: + """Get a specific item in the list.""" + return self.__items[n] + + def get_range(self, date: datetime.date) -> tuple[datetime.datetime, + datetime.datetime]: + """Get the minimum and maximum timestamps for the date.""" + min = datetime.datetime.combine(date, datetime.time()) + max = min + datetime.timedelta(days=1) + return (min, max)