Compare commits

..

No commits in common. "06d10cf8837585905fbf52b56b72388d04fdd21c" and "528444cab683e6255adb520e7caf21c614c40367" have entirely different histories.

19 changed files with 36 additions and 1206 deletions

View File

@ -1,6 +1,5 @@
# Copyright 2023 (c) Anna Schumaker.
"""Tests our row widgets and factories."""
import datetime
import unittest
import xfstestsdb.gtk.row
import tests.xunit
@ -376,67 +375,3 @@ class TestSummaryFactory(unittest.TestCase):
self.factory.emit("unbind", self.listitem)
self.assertFalse(self.listitem.get_child().has_css_class("accent"))
class TestSidebarFactory(unittest.TestCase):
"""Tests our Gtk.Factory to show Xfstsets runs in the sidebar."""
def setUp(self):
"""Set up common variables."""
self.devlist = xfstestsdb.gtk.tree.DeviceRunsList("/dev/vda1")
self.treeitem = Gtk.TreeListRow()
self.treeitem.get_item = unittest.mock.Mock(return_value=self.devlist)
self.listitem = Gtk.ListItem()
self.listitem.get_item = unittest.mock.Mock(return_value=self.treeitem)
self.factory = xfstestsdb.gtk.row.SidebarFactory()
def test_init(self):
"""Test that the factory was initialized correctly."""
self.assertIsInstance(self.factory, Gtk.SignalListItemFactory)
def test_setup(self):
"""Test that thefactory implements the 'setup' signal."""
self.factory.emit("setup", self.listitem)
expander = self.listitem.get_child()
self.assertIsInstance(expander, Gtk.TreeExpander)
self.assertIsInstance(expander.get_child(), Gtk.Label)
self.assertEqual(expander.get_child().props.yalign, 0.75)
self.assertTrue(expander.get_child().has_css_class("numeric"))
def test_bind_device_list(self):
"""Test binding to a DeviceRunsList object."""
self.factory.emit("setup", self.listitem)
self.factory.emit("bind", self.listitem)
self.assertEqual(self.listitem.get_child().get_child().get_text(),
"/dev/vda1")
self.assertEqual(self.listitem.get_child().get_list_row(),
self.treeitem)
self.assertFalse(self.listitem.props.selectable)
def test_bind_xfstests_run(self):
"""Test binding to an XfstestsRun object."""
now = datetime.datetime.now()
self.devlist.add_run(1, now)
self.treeitem.get_item.return_value = self.devlist[0]
self.factory.emit("setup", self.listitem)
self.factory.emit("bind", self.listitem)
self.assertEqual(self.listitem.get_child().get_child().get_text(),
f"#1: {now.strftime('%T')}")
self.assertEqual(self.listitem.get_child().get_list_row(),
self.treeitem)
self.assertTrue(self.listitem.props.selectable)
def test_unbind(self):
"""Test that the factory implements the 'unbind' signal."""
self.factory.emit("setup", self.listitem)
self.factory.emit("bind", self.listitem)
self.factory.emit("unbind", self.listitem)
self.assertEqual(self.listitem.get_child().get_child().get_text(), "")
def test_teardown(self):
"""Test that the factory implements the 'teardown' signal."""
self.factory.emit("setup", self.listitem)
self.factory.emit("teardown", self.listitem)
self.assertIsNone(self.listitem.get_child())

View File

@ -1,246 +0,0 @@
# Copyright 2023 (c) Anna Schumaker.
"""Tests our sidebar test selector."""
import datetime
import unittest
import xfstestsdb.gtk.sidebar
from gi.repository import GLib
from gi.repository import Gtk
class TestRunidView(unittest.TestCase):
"""Test the RunidView class."""
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.today = datetime.date.today()
self.devlist = xfstestsdb.gtk.tree.DateDeviceList(self.xfstestsdb.sql,
self.today)
self.view = xfstestsdb.gtk.sidebar.RunidView(self.devlist)
def test_init(self):
"""Test that the RunidView was set up properly."""
self.assertIsInstance(self.view, Gtk.ScrolledWindow)
self.assertEqual(self.view.props.vexpand, True)
def test_listview(self):
"""Test that the listview child is set up properly."""
self.assertIsInstance(self.view._view, Gtk.ListView)
self.assertIsInstance(self.view._selection, Gtk.SingleSelection)
self.assertIsInstance(self.view._view.props.factory,
xfstestsdb.gtk.row.SidebarFactory)
self.assertTrue(self.view._view.props.single_click_activate)
self.assertTrue(self.view._view.has_css_class("navigation-sidebar"))
self.assertTrue(self.view._view.has_css_class("background"))
self.assertEqual(self.view._view.props.model,
self.view._selection)
self.assertEqual(self.view.props.child, self.view._view)
def test_model_property(self):
"""Test the model property."""
self.assertEqual(self.view.model, self.devlist)
self.assertEqual(self.view._selection.props.model,
self.devlist.treemodel)
self.view.model = None
self.assertIsNone(self.view._selection.props.model)
self.view.model = self.devlist
self.assertEqual(self.view._selection.props.model,
self.devlist.treemodel)
def test_runid_property(self):
"""Test the runid property."""
self.view.model = self.devlist
self.assertEqual(self.view.runid, 0)
self.view._view.emit("activate", 1)
self.assertEqual(self.view.runid, 2)
self.view._view.emit("activate", 2)
self.assertEqual(self.view.runid, 3)
self.view._view.emit("activate", 4)
self.assertEqual(self.view.runid, 1)
self.view._view.emit("activate", 6)
self.assertEqual(self.view.runid, 4)
def test_expand_collapse(self):
"""Test expanding and collapsing child rows."""
self.view.model = self.devlist
self.assertTrue(self.view._selection[0].get_expanded())
self.assertEqual(self.view.runid, 0)
self.view._view.emit("activate", 0)
self.assertFalse(self.view._selection[0].get_expanded())
self.assertEqual(self.view.runid, 0)
self.view._view.emit("activate", 0)
self.assertTrue(self.view._selection[0].get_expanded())
self.assertEqual(self.view.runid, 2)
class TestCalendarView(unittest.TestCase):
"""Test the CalendarView class."""
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(["new", "/dev/vda2"])
self.xfstestsdb.run(["new", "/dev/vda3"])
self.xfstestsdb.run(["new", "/dev/vda4"])
query = "UPDATE xfstests_runs SET timestamp=? WHERE runid=?"
dtime = datetime.datetime.now().replace(year=2023, month=1)
self.xfstestsdb.sql(query, dtime.replace(day=1), 1)
self.xfstestsdb.sql(query, dtime.replace(day=8), 2)
self.xfstestsdb.sql(query, dtime.replace(day=15), 3)
self.sidebar = xfstestsdb.gtk.sidebar.CalendarView(self.xfstestsdb.sql)
def test_init(self):
"""Test that the calendar view was set up properly."""
self.assertIsInstance(self.sidebar, Gtk.Box)
self.assertEqual(self.sidebar.props.spacing, 6)
self.assertEqual(self.sidebar.props.orientation,
Gtk.Orientation.VERTICAL)
self.assertEqual(self.sidebar.sql, self.xfstestsdb.sql)
def test_calendar(self):
"""Test the calendar widget."""
self.assertIsInstance(self.sidebar._calendar, Gtk.Calendar)
self.assertEqual(self.sidebar.get_first_child(),
self.sidebar._calendar)
today = datetime.date.today()
self.assertTrue(self.sidebar._calendar.get_day_is_marked(today.day))
def test_view(self):
"""Test the runid view widget."""
self.assertIsInstance(self.sidebar._view,
xfstestsdb.gtk.sidebar.RunidView)
self.assertIsInstance(self.sidebar._view.model,
xfstestsdb.gtk.tree.DateDeviceList)
self.assertEqual(self.sidebar._calendar.get_next_sibling(),
self.sidebar._view)
self.assertEqual(self.sidebar._view.model.date, datetime.date.today())
def test_marked_days(self):
"""Test marking days in the calendar."""
gl_date = GLib.DateTime.new_local(2023, 1, 10, 0, 0, 0)
self.sidebar._calendar.select_day(gl_date)
for signal in ["next-month", "next-year", "prev-month", "prev-year"]:
with self.subTest(signal=signal):
with unittest.mock.patch.object(self.sidebar._calendar,
"clear_marks") as mock_clear:
with unittest.mock.patch.object(self.sidebar._calendar,
"mark_day") as mock_mark:
self.sidebar._calendar.emit(signal)
mock_clear.assert_called()
mock_mark.assert_has_calls([unittest.mock.call(1),
unittest.mock.call(8),
unittest.mock.call(15)])
def test_select_day(self):
"""Test selecting a day in the calendar."""
gl_now = GLib.DateTime.new_now_local()
self.sidebar._calendar.select_day(gl_now.add_days(-1))
today = datetime.date.today()
self.assertEqual(self.sidebar._view.model.date,
today - datetime.timedelta(days=1))
def test_runid_property(self):
"""Test the runid property."""
self.assertEqual(self.sidebar.runid, 0)
self.sidebar._view.runid = 42
self.assertEqual(self.sidebar.runid, 42)
class TestSidebar(unittest.TestCase):
"""Test the Sidebar class."""
def setUp(self):
"""Set up common variables."""
self.xfstestsdb = xfstestsdb.Command()
self.sidebar = xfstestsdb.gtk.sidebar.Sidebar(self.xfstestsdb.sql)
def test_init(self):
"""Test taht the sidebar was set up properly."""
self.assertIsInstance(self.sidebar, Gtk.Box)
self.assertEqual(self.sidebar.props.orientation,
Gtk.Orientation.VERTICAL)
self.assertEqual(self.sidebar.sql, self.xfstestsdb.sql)
def test_stack_switcher(self):
"""Test the Gtk.StackSwitcher."""
self.assertIsInstance(self.sidebar._switcher, Gtk.StackSwitcher)
self.assertEqual(self.sidebar._switcher.props.stack,
self.sidebar._stack)
self.assertTrue(self.sidebar._switcher.has_css_class("large-icons"))
self.assertEqual(self.sidebar._switcher.props.margin_top, 6)
self.assertEqual(self.sidebar._switcher.props.margin_bottom, 6)
self.assertEqual(self.sidebar._switcher.props.margin_start, 80)
self.assertEqual(self.sidebar._switcher.props.margin_end, 80)
self.assertEqual(self.sidebar.get_first_child(),
self.sidebar._switcher)
def test_separator(self):
"""Test the Gtk.Separator between sidebar widgets."""
sep = self.sidebar._switcher.get_next_sibling()
self.assertIsInstance(sep, Gtk.Separator)
self.assertEqual(sep.props.orientation, Gtk.Orientation.HORIZONTAL)
self.assertEqual(sep.get_next_sibling(), self.sidebar._stack)
def test_stack(self):
"""Test the Gtk.Stack."""
self.assertIsInstance(self.sidebar._stack, Gtk.Stack)
self.assertEqual(self.sidebar._stack.props.transition_type,
Gtk.StackTransitionType.OVER_LEFT_RIGHT)
self.assertEqual(self.sidebar.get_last_child(), self.sidebar._stack)
def test_calendar_page(self):
"""Test the Sidebar calendar view page."""
self.assertIsInstance(self.sidebar._calendar,
xfstestsdb.gtk.sidebar.CalendarView)
self.assertEqual(self.sidebar._calendar.sql, self.xfstestsdb.sql)
self.assertEqual(self.sidebar._stack.get_child_by_name("calendar"),
self.sidebar._calendar)
page = self.sidebar._stack.get_page(self.sidebar._calendar)
self.assertEqual(page.props.title, "Calendar")
self.assertEqual(page.props.icon_name, "month-symbolic")
def test_tag_page(self):
"""Test the Sidebar tag view page."""
self.assertIsInstance(self.sidebar._tags,
xfstestsdb.gtk.sidebar.RunidView)
self.assertEqual(self.sidebar._stack.get_child_by_name("tags"),
self.sidebar._tags)
page = self.sidebar._stack.get_page(self.sidebar._tags)
self.assertEqual(page.props.title, "Tags")
self.assertEqual(page.props.icon_name, "tag-symbolic")
def test_runid(self):
"""Test the runid property."""
self.assertEqual(self.sidebar.runid, 0)
self.sidebar._calendar.runid = 1
self.assertEqual(self.sidebar.runid, 1)
self.sidebar._tags.runid = 2
self.assertEqual(self.sidebar.runid, 2)

View File

@ -1,330 +0,0 @@
# Copyright 2023 (c) Anna Schumaker.
"""Tests our xfstests run selector tree."""
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):
"""Test case for our XfstestsRun GObject."""
def setUp(self):
"""Set up common variables."""
self.now = datetime.datetime.now()
self.run = xfstestsdb.gtk.tree.XfstestsRun(1, self.now)
def test_run(self):
"""Test the XfsetstsRun object."""
self.assertIsInstance(self.run, GObject.GObject)
self.assertEqual(self.run.runid, 1)
self.assertEqual(self.run.timestamp, self.now)
def test_gt(self):
"""Test the XfstestsRun Greater-Than operator."""
soon = self.now + datetime.timedelta(seconds=5)
run2 = xfstestsdb.gtk.tree.XfstestsRun(2, soon)
run3 = xfstestsdb.gtk.tree.XfstestsRun(3, soon)
self.assertTrue(run2 > self.run)
self.assertFalse(self.run > run2)
self.assertFalse(self.run > self.run)
self.assertTrue(run3 > run2)
self.assertFalse(run2 > run3)
def test_lt(self):
"""Test the XfstestsRun Less-Than operator."""
soon = self.now + datetime.timedelta(seconds=5)
run2 = xfstestsdb.gtk.tree.XfstestsRun(2, soon)
run3 = xfstestsdb.gtk.tree.XfstestsRun(3, soon)
self.assertTrue(self.run < run2)
self.assertFalse(run2 < self.run)
self.assertFalse(self.run < self.run)
self.assertTrue(run2 < run3)
self.assertFalse(run3 < run2)
def test_str(self):
"""Test converting an XfstestsRun to a string."""
self.assertEqual(self.run.ftime, "%T")
self.assertEqual(str(self.run), f"#1: {self.now.strftime('%T')}")
self.run.ftime = "%c"
self.assertEqual(str(self.run), f"#1: {self.now.strftime('%c')}")
class TestDeviceRunModel(unittest.TestCase):
"""Test case for our DeviceRow GObject."""
def setUp(self):
"""Set up common variables."""
self.device = xfstestsdb.gtk.tree.DeviceRunsList(name="/dev/vda1")
def test_init(self):
"""Test creating a DeviceRow instance."""
self.assertIsInstance(self.device, GObject.GObject)
self.assertIsInstance(self.device, Gio.ListModel)
self.assertEqual(self.device.name, "/dev/vda1")
self.assertEqual(str(self.device), "/dev/vda1")
def test_lt(self):
"""Test comparing DeviceRow instances."""
dev_a = xfstestsdb.gtk.tree.DeviceRunsList(name="a")
dev_b = xfstestsdb.gtk.tree.DeviceRunsList(name="b")
self.assertTrue(dev_a < dev_b)
self.assertFalse(dev_b < dev_a)
self.assertFalse(dev_a < dev_a)
def test_get_item_type(self):
"""Test the get_item_type() function."""
self.assertEqual(self.device.get_item_type(),
xfstestsdb.gtk.tree.XfstestsRun.__gtype__)
def test_get_n_items(self):
"""Test the get_n_items() function."""
self.assertEqual(self.device.get_n_items(), 0)
self.assertEqual(self.device.n_items, 0)
self.device.add_run(1, datetime.datetime.now())
self.assertEqual(self.device.get_n_items(), 1)
self.assertEqual(self.device.n_items, 1)
def test_get_item(self):
"""Test the get_item() function."""
now = datetime.datetime.now()
then = now - datetime.timedelta(seconds=42)
self.device.add_run(1, now)
self.assertIsInstance(self.device[0], xfstestsdb.gtk.tree.XfstestsRun)
self.assertEqual(self.device[0].runid, 1)
self.assertEqual(self.device[0].timestamp, now)
self.device.add_run(2, then, "%c")
self.assertEqual(self.device[0].runid, 2)
self.assertEqual(self.device[0].ftime, "%c")
self.assertEqual(self.device[1].runid, 1)
def test_get_earliest_run(self):
"""Test the get_earliest_run() function."""
self.assertIsNone(self.device.get_earliest_run())
now = datetime.datetime.now()
self.device.add_run(1, now)
self.assertEqual(self.device.get_earliest_run().runid, 1)
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])
class TestTagDeviceList(unittest.TestCase):
"""Test case for our TagDeviceList GObject."""
def setUp(self):
"""Set up common variables."""
self.tag = xfstestsdb.gtk.tree.TagDeviceList(name="mytag")
def test_init(self):
"""Test creating a TagDeviceList instance."""
self.assertIsInstance(self.tag, GObject.GObject)
self.assertIsInstance(self.tag, Gio.ListModel)
self.assertEqual(self.tag.name, "mytag")
self.assertEqual(str(self.tag), "mytag")
def test_lt(self):
"""Test comparing TagDeviceList instances."""
tag_a = xfstestsdb.gtk.tree.TagDeviceList(name="a")
tag_b = xfstestsdb.gtk.tree.TagDeviceList(name="b")
now = datetime.datetime.now()
then = now - datetime.timedelta(seconds=42)
tag_a.add_run(1, "/dev/vda1", now)
tag_b.add_run(2, "/dev/vda2", then)
self.assertTrue(tag_a < tag_b)
self.assertFalse(tag_b < tag_a)
self.assertFalse(tag_a < tag_a)
tag_a.add_run(2, "/dev/vda2", then)
self.assertTrue(tag_b < tag_a)
def test_get_item_type(self):
"""Test the get_item_type() function."""
self.assertEqual(self.tag.get_item_type(),
xfstestsdb.gtk.tree.DeviceRunsList.__gtype__)
def test_get_n_items(self):
"""Test the get_n_items() function."""
self.assertEqual(self.tag.get_n_items(), 0)
self.assertEqual(self.tag.n_items, 0)
self.tag.add_run(1, "/dev/vda1", datetime.datetime.now())
self.assertEqual(self.tag.get_n_items(), 1)
self.assertEqual(self.tag.n_items, 1)
def test_get_item(self):
"""Test the get_item() function."""
now = datetime.datetime.now()
then = now - datetime.timedelta(seconds=42)
self.tag.add_run(1, "/dev/vda2", now)
self.assertIsInstance(self.tag[0], xfstestsdb.gtk.tree.DeviceRunsList)
self.assertEqual(self.tag[0].name, "/dev/vda2")
self.assertEqual(self.tag[0][0].runid, 1)
self.assertEqual(self.tag[0][0].timestamp, now)
self.assertEqual(self.tag[0][0].ftime, "%c")
self.tag.add_run(2, "/dev/vda1", then)
self.assertEqual(self.tag[0].name, "/dev/vda1")
self.assertEqual(self.tag[1].name, "/dev/vda2")
def test_get_earliest_run(self):
"""Test the get_earliest_run() function."""
self.assertIsNone(self.tag.get_earliest_run())
now = datetime.datetime.now()
self.tag.add_run(1, "/dev/vda1", now)
self.assertEqual(self.tag.get_earliest_run().runid, 1)
then = now - datetime.timedelta(seconds=42)
self.tag.add_run(2, "/dev/vda2", then)
self.assertEqual(self.tag.get_earliest_run().runid, 2)
class TestTagList(unittest.TestCase):
"""Test case for our TagList ListModel."""
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(["tag", "1", "mytag2"])
self.xfstestsdb.run(["tag", "2", "mytag2"])
self.xfstestsdb.run(["tag", "4", "mytag1"])
self.taglist = xfstestsdb.gtk.tree.TagList(self.xfstestsdb.sql)
def test_init(self):
"""Test that the TagList was set up properly."""
self.assertIsInstance(self.taglist, GObject.GObject)
self.assertIsInstance(self.taglist, Gio.ListModel)
def test_get_item_type(self):
"""Test the get_item_type() function."""
self.assertEqual(self.taglist.get_item_type(),
xfstestsdb.gtk.tree.TagDeviceList.__gtype__)
def test_get_n_items(self):
"""Test the get_n_items() function."""
self.assertEqual(self.taglist.get_n_items(), 2)
self.assertEqual(self.taglist.n_items, 2)
def test_get_item(self):
"""Test the get_item() function."""
self.assertIsInstance(self.taglist.get_item(0),
xfstestsdb.gtk.tree.TagDeviceList)
self.assertIsInstance(self.taglist[0][0][0].timestamp,
datetime.datetime)
self.assertEqual(self.taglist.get_item(0).name, "mytag1")
self.assertEqual(self.taglist.get_item(1).name, "mytag2")
def test_treemodel(self):
"""Test the treemodel property."""
self.assertIsInstance(self.taglist.treemodel, Gtk.TreeListModel)
self.assertFalse(self.taglist.treemodel.props.passthrough)
self.assertFalse(self.taglist.treemodel.props.autoexpand)
tree = self.taglist.treemodel
self.assertEqual(tree[0].get_item().name, "mytag1")
tree[0].set_expanded(True)
self.assertEqual(tree[1].get_item().name, "/dev/vda3")
tree[1].set_expanded(True)
self.assertEqual(tree[2].get_item().runid, 4)
self.assertEqual(tree[3].get_item().name, "mytag2")
tree[3].set_expanded(True)
self.assertEqual(tree[4].get_item().name, "/dev/vda1")
self.assertEqual(tree[5].get_item().name, "/dev/vda2")

View File

@ -26,8 +26,6 @@ class TestXunitView(unittest.TestCase):
def test_init(self):
"""Test that we created the XunitView correctly."""
self.assertIsInstance(self.view, Gtk.ScrolledWindow)
self.assertTrue(self.view.has_css_class("undershoot-top"))
self.assertTrue(self.view.has_css_class("undershoot-bottom"))
self.assertTrue(self.view.has_css_class("card"))
def test_columnview(self):
@ -385,8 +383,8 @@ class MessagesView(unittest.TestCase):
self.view._back)
self.assertEqual(self.view._back.props.child.props.icon_name,
"down-large-symbolic")
self.assertEqual(self.view._back.props.child.props.label, "done")
"go-previous-symbolic")
self.assertEqual(self.view._back.props.child.props.label, "back")
self.assertTrue(self.view._back.has_css_class("suggested-action"))
self.assertTrue(self.view._back.has_css_class("pill"))
@ -502,7 +500,7 @@ class TestXfstestsView(unittest.TestCase):
self.assertIsInstance(sep, Gtk.Separator)
self.assertIsInstance(self.view._stack, Gtk.Stack)
self.assertEqual(self.view._stack.props.transition_type,
Gtk.StackTransitionType.OVER_UP_DOWN)
Gtk.StackTransitionType.OVER_LEFT_RIGHT)
self.assertEqual(sep.get_next_sibling(), self.view._stack)
def test_testcase_view(self):

View File

@ -38,61 +38,21 @@ class TestWindow(unittest.TestCase):
self.assertEqual(self.window.headerbar.props.title_widget,
self.window.title)
def test_splitview(self):
"""Check that the Window's splitview is set up correctly."""
self.assertIsInstance(self.window._splitview, Adw.OverlaySplitView)
self.assertEqual(self.window.headerbar.get_next_sibling(),
self.window._splitview)
self.assertTrue(self.window._splitview.props.collapsed)
def test_child(self):
"""Test the window child property."""
self.assertIsInstance(self.window.headerbar.get_next_sibling(),
Adw.Bin)
def test_show_sidebar(self):
"""Test the window show sidebar button."""
self.assertIsInstance(self.window._show_sidebar, Gtk.ToggleButton)
self.assertEqual(self.window._show_sidebar.get_ancestor(Adw.HeaderBar),
self.window.headerbar)
self.assertEqual(self.window._show_sidebar.props.icon_name,
"sidebar-show")
self.assertFalse(self.window.show_sidebar)
self.assertFalse(self.window._show_sidebar.props.active)
self.assertFalse(self.window._splitview.props.show_sidebar)
self.window._show_sidebar.props.active = True
self.assertTrue(self.window._splitview.props.show_sidebar)
self.assertTrue(self.window.show_sidebar)
self.window.show_sidebar = False
self.assertFalse(self.window._show_sidebar.props.active)
self.assertFalse(self.window._splitview.props.show_sidebar)
window2 = xfstestsdb.gtk.window.Window(show_sidebar=True)
self.assertTrue(window2.show_sidebar)
self.assertTrue(window2._show_sidebar.props.active)
self.assertTrue(window2._splitview.props.show_sidebar)
def test_content(self):
"""Test the window content property."""
self.assertIsNone(self.window.child)
self.window.child = Gtk.Label()
self.assertEqual(self.window._splitview.props.content,
self.assertEqual(self.window.headerbar.get_next_sibling().props.child,
self.window.child)
label = Gtk.Label()
window2 = xfstestsdb.gtk.window.Window(child=label)
self.assertEqual(window2.child, label)
self.assertEqual(window2._splitview.props.content, label)
def test_sidebar(self):
"""Test the window sidebar property."""
self.assertIsNone(self.window.sidebar)
self.window.sidebar = Gtk.Label()
self.assertEqual(self.window._splitview.props.sidebar,
self.window.sidebar)
label = Gtk.Label()
window2 = xfstestsdb.gtk.window.Window(sidebar=label)
self.assertEqual(window2.sidebar, label)
self.assertEqual(window2._splitview.props.sidebar, label)
self.assertEqual(window2.headerbar.get_next_sibling().props.child,
label)
def test_runid(self):
"""Test the window runid property."""

View File

@ -44,21 +44,11 @@ class TestApplication(unittest.TestCase):
mock_cmd.get_arguments.assert_called()
mock_activate.assert_called()
self.assertEqual(self.application.runid, 0)
self.assertFalse(self.application.show_sidebar)
self.assertIsNone(self.application.environment)
self.assertIsNone(self.application.properties)
self.assertIsNone(self.application.model)
self.assertIsNone(self.application.summary)
mock_command_line.reset_mock()
mock_activate.reset_mock()
mock_cmd.reset_mock()
mock_cmd.get_arguments.return_value = ["show-sidebar"]
self.application.do_command_line(mock_cmd)
self.assertTrue(self.application.show_sidebar)
self.application.show_sidebar = False
mock_command_line.reset_mock()
mock_activate.reset_mock()
mock_cmd.reset_mock()
@ -78,7 +68,6 @@ class TestApplication(unittest.TestCase):
self.assertEqual(self.application.environment,
self.application.properties.environment)
self.assertEqual(self.application.model.runid, 42)
self.assertFalse(self.application.show_sidebar)
@unittest.mock.patch("xfstestsdb.gtk.gsetup.add_style")
@unittest.mock.patch("gi.repository.Adw.Application.add_window")
@ -88,33 +77,22 @@ class TestApplication(unittest.TestCase):
mock_add_style: unittest.mock.Mock):
"""Check that startup sets up our application instance correctly."""
self.assertIsNone(self.application.win)
self.assertIsNone(self.application.sidebar)
self.assertIsNone(self.application.view)
self.application.emit("startup")
self.assertIsInstance(self.application.sidebar,
xfstestsdb.gtk.sidebar.Sidebar)
self.assertIsInstance(self.application.view,
xfstestsdb.gtk.view.XfstestsView)
self.assertIsInstance(self.application.win,
xfstestsdb.gtk.window.Window)
self.assertEqual(self.application.win.child, self.application.view)
self.assertEqual(self.application.win.sidebar,
self.application.sidebar)
self.assertEqual(self.application.sidebar.sql,
self.application.sql)
mock_startup.assert_called_with(self.application)
mock_add_window.assert_called_with(self.application.win)
mock_add_style.assert_called()
self.application.sidebar.runid = 42
self.assertEqual(self.application.runid, 42)
self.application.runid = 42
self.assertEqual(self.application.win.runid, 42)
self.application.show_sidebar = True
self.assertTrue(self.application.win.show_sidebar)
properties = xfstestsdb.gtk.model.PropertyList(self.xfstestsdb.sql, 42)
self.application.properties = properties
self.assertEqual(self.application.view.properties, properties)
@ -172,9 +150,11 @@ class TestGtk(unittest.TestCase):
def test_gtk_empty(self, mock_app: unittest.mock.Mock,
mock_stderr: unittest.mock.Mock):
"""Check that running `xfstestsdb gtk` without a runid."""
self.xfstestsdb.run(["gtk"])
mock_app.assert_called_with(self.xfstestsdb.sql)
mock_app.return_value.run.assert_called_with(["show-sidebar"])
with self.assertRaises(SystemExit):
self.xfstestsdb.run(["gtk"])
self.assertRegex(mock_stderr.getvalue(),
"error: the following arguments are required: runid")
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
@unittest.mock.patch("xfstestsdb.gtk.Application")

View File

@ -52,7 +52,7 @@ class TestXfstestsdb(unittest.TestCase):
def test_version(self, mock_stdout: io.StringIO):
"""Test printing version information."""
self.assertEqual(xfstestsdb.MAJOR, 1)
self.assertEqual(xfstestsdb.MINOR, 6)
self.assertEqual(xfstestsdb.MINOR, 5)
self.xfstestsdb.run(["--version"])
self.assertEqual(mock_stdout.getvalue(), "xfstestsdb v1.6-debug\n")
self.assertEqual(mock_stdout.getvalue(), "xfstestsdb v1.5-debug\n")

View File

@ -15,7 +15,7 @@ from . import untag
from . import xunit
MAJOR = 1
MINOR = 6
MINOR = 5
class Command:

View File

@ -8,7 +8,6 @@ from gi.repository import GObject
from gi.repository import Gio
from gi.repository import Adw
from . import model
from . import sidebar
from . import view
from . import window
from .. import commands
@ -19,13 +18,11 @@ class Application(Adw.Application):
"""Our Adw.Application for displaying xfstests results."""
runid = GObject.Property(type=int)
show_sidebar = GObject.Property(type=bool, default=False)
environment = GObject.Property(type=Gio.ListStore)
properties = GObject.Property(type=model.PropertyList)
summary = GObject.Property(type=model.SummaryList)
model = GObject.Property(type=model.TestCaseList)
win = GObject.Property(type=window.Window)
sidebar = GObject.Property(type=sidebar.Sidebar)
view = GObject.Property(type=view.XfstestsView)
sql = GObject.Property(type=GObject.TYPE_PYOBJECT)
@ -35,14 +32,6 @@ class Application(Adw.Application):
resource_base_path=gsetup.RESOURCE_PATH,
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
sql=sql)
self.connect("notify::runid", self.__notify_runid)
def __notify_runid(self, app: Adw.Application,
param: GObject.ParamSpec) -> None:
self.properties = model.PropertyList(self.sql, self.runid)
self.environment = self.properties.environment
self.model = model.TestCaseList(self.sql, self.runid)
self.summary = model.SummaryList(self.sql, self.runid)
def do_command_line(self, cmd_line: Gio.ApplicationCommandLine) -> int:
"""Handle the Adw.Application::command-line signal."""
@ -53,8 +42,10 @@ class Application(Adw.Application):
match split[0]:
case "runid":
self.runid = int(split[1])
case "show-sidebar":
self.show_sidebar = True
self.properties = model.PropertyList(self.sql, self.runid)
self.environment = self.properties.environment
self.model = model.TestCaseList(self.sql, self.runid)
self.summary = model.SummaryList(self.sql, self.runid)
self.activate()
return 0
@ -64,14 +55,11 @@ class Application(Adw.Application):
Adw.Application.do_startup(self)
gsetup.add_style()
self.sidebar = sidebar.Sidebar(self.sql)
self.view = view.XfstestsView()
self.win = window.Window(child=self.view, sidebar=self.sidebar)
self.win = window.Window(child=self.view)
self.win.headerbar.pack_end(self.view.filterbuttons)
self.sidebar.bind_property("runid", self, "runid")
self.bind_property("runid", self.win, "runid")
self.bind_property("show-sidebar", self.win, "show-sidebar")
self.bind_property("environment", self.view, "environment")
self.bind_property("properties", self.view, "properties")
self.bind_property("model", self.view, "model")
@ -95,22 +83,19 @@ class Command(commands.Command):
sql: sqlite.Connection) -> None:
"""Set up the gtk command."""
super().__init__(subparser, sql, "gtk", help="show a gtk-based ui")
self.parser.add_argument("runid", metavar="runid", nargs='?', type=int,
self.parser.add_argument("runid", metavar="runid", nargs=1, type=int,
help="show a specific xfstests run")
def do_command(self, args: argparse.Namespace) -> None:
"""Run the Gtk UI."""
app_args = []
if args.runid is not None:
if self.sql("SELECT 1 FROM xfstests_runs WHERE runid=?",
args.runid).fetchone():
app_args.append(f"runid={args.runid}")
else:
print(f"error: run #{args.runid} does not exist",
file=sys.stderr)
sys.exit(errno.ENOENT)
if self.sql("SELECT 1 FROM xfstests_runs WHERE runid=?",
args.runid[0]).fetchone():
app_args.append(f"runid={args.runid[0]}")
else:
app_args.append("show-sidebar")
print(f"error: run #{args.runid[0]} does not exist",
file=sys.stderr)
sys.exit(errno.ENOENT)
Application(self.sql).run(app_args)

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m -0.00390625 3 v 1 c 0 0.292969 0.12890625 0.558594 0.32812525 0.738281 l 7.671875 7.675781 l 7.707031 -7.707031 c 0.183594 -0.179687 0.292969 -0.429687 0.292969 -0.707031 v -1 h -1 c -0.273438 0 -0.523438 0.113281 -0.707032 0.292969 c -0.007812 0.011719 -0.019531 0.019531 -0.03125 0.03125 l -6.261718 6.261719 l -6.257813 -6.261719 c -0.183593 -0.199219 -0.445312 -0.324219 -0.742187 -0.324219 z m 0 0"/></svg>

Before

Width:  |  Height:  |  Size: 549 B

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 3.042969 1 c -1.128907 0 -2.042969 0.914062 -2.042969 2.042969 v 9.875 c 0 1.132812 0.914062 2.042969 2.042969 2.042969 h 9.914062 c 1.128907 0 2.042969 -0.910157 2.042969 -2.042969 v -9.875 c 0 -1.128907 -0.914062 -2.042969 -2.042969 -2.042969 z m -0.042969 4.960938 h 10 v 7 h -10 z m 1 1.039062 v 2 h 2 v -2 z m 3 0 v 2 h 2 v -2 z m 3 0 v 2 h 2 v -2 z m -6 3 v 2 h 2 v -2 z m 3 0 v 2 h 2 v -2 z m 0 0"/></svg>

Before

Width:  |  Height:  |  Size: 550 B

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg7384"
height="16"
width="16"
version="1.1">
<title
id="title9167">Gnome Symbolic Icon Theme</title>
<defs
id="defs9" />
<metadata
id="metadata90">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Gnome Symbolic Icon Theme</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<path
id="path855"
d="m 1,1 v 6.0008052 l 7.9783226,7.9783228 c 0.5857862,0.585786 1.5355344,0.585786 2.1213204,0 l 0.0097,-0.0097 3.87942,-3.87942 0.0097,-0.0097 c 0.585786,-0.585786 0.585786,-1.535534 0,-2.1213204 L 7.0394752,1 Z m 2.4775289,2.4403054 c 0.271447,-0.271447 0.6464468,-0.4391799 1.0606602,-0.4391797 0.8284268,3e-7 1.4998395,0.671413 1.4998398,1.4998398 4e-7,0.8284269 -0.6714131,1.4998403 -1.4998399,1.49984 -0.8284275,4e-7 -1.4998402,-0.6714123 -1.4998398,-1.4998399 -2e-7,-0.4142134 0.1677334,-0.7892139 0.4391797,-1.0606602 z"
style="display:inline;fill:#bebebe;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1">
<title
id="title864">tag-symbolic</title>
</path>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -4,8 +4,5 @@
<file preprocess="xml-stripblanks">test-pass-symbolic.svg</file>
<file preprocess="xml-stripblanks">test-skip-symbolic.svg</file>
<file preprocess="xml-stripblanks">test-fail-symbolic.svg</file>
<file preprocess="xml-stripblanks">down-large-symbolic.svg</file>
<file preprocess="xml-stripblanks">month-symbolic.svg</file>
<file preprocess="xml-stripblanks">tag-symbolic.svg</file>
</gresource>
</gresources>

View File

@ -4,7 +4,6 @@ import typing
from gi.repository import GObject
from gi.repository import Gtk
from . import model
from . import tree
STYLES = {"passed": "success", "failed": "error",
@ -182,40 +181,3 @@ class SummaryFactory(XunitFactory):
def do_unbind(self, row: model.TestCase, child: Gtk.Inscription) -> None:
"""Unbind a ListItem from the child widget."""
child.remove_css_class(STYLES[row.name])
class SidebarFactory(Gtk.SignalListItemFactory):
"""Factory for making sidebar widgets."""
def __init__(self, *args, **kwargs):
"""Initialize our InscriptionFactory."""
super().__init__(*args, **kwargs)
self.connect("setup", self.__setup)
self.connect("bind", self.__bind)
self.connect("unbind", self.__unbind)
self.connect("teardown", self.__teardown)
def __setup(self, factory: typing.Self, listitem: Gtk.ListItem) -> None:
"""Set up a ListItem child widget."""
child = Gtk.Label(yalign=0.75)
child.add_css_class("numeric")
expander = Gtk.TreeExpander(child=child)
listitem.set_child(expander)
def __bind(self, factory: typing.Self, listitem: Gtk.ListItem) -> None:
"""Bind a ListItem to the child widget."""
treeitem = listitem.get_item()
expander = listitem.get_child()
expander.set_list_row(treeitem)
row = treeitem.get_item()
child = expander.get_child()
child.set_text(str(row))
listitem.props.selectable = isinstance(row, tree.XfstestsRun)
def __unbind(self, factory: typing.Self, listitem: Gtk.ListItem) -> None:
"""Unbind a ListItem from the child widget."""
listitem.get_child().get_child().set_text("")
def __teardown(self, factory: typing.Self, listitem: Gtk.ListItem) -> None:
listitem.set_child(None)

View File

@ -1,131 +0,0 @@
# Copyright 2023 (c) Anna Schumaker.
"""Our sidebar for selecting a specific xfstests run to view."""
import datetime
import typing
from gi.repository import GObject
from gi.repository import Gio
from gi.repository import Gtk
from .. import sqlite
from . import row
from . import tree
class RunidView(Gtk.ScrolledWindow):
"""Our RunidView class."""
runid = GObject.Property(type=int)
model = GObject.Property(type=Gio.ListModel)
def __init__(self, model: tree.DateDeviceList) -> None:
"""Initialize a RunidView instance."""
super().__init__(model=model, vexpand=True)
self._selection = Gtk.SingleSelection(model=model.treemodel)
self._view = Gtk.ListView(model=self._selection,
factory=row.SidebarFactory(),
single_click_activate=True)
self._view.connect("activate", self.__activate)
self.connect("notify::model", self.__notify_model)
self._view.add_css_class("navigation-sidebar")
self._view.add_css_class("background")
self.set_child(self._view)
def __activate(self, view: Gtk.ListView, position: int) -> None:
treeitem = self._selection[position]
item = treeitem.get_item()
if isinstance(item, tree.XfstestsRun):
self.runid = item.runid
elif treeitem.props.expanded:
treeitem.props.expanded = False
else:
treeitem.props.expanded = True
self.runid = item.get_earliest_run().runid
def __notify_model(self, sidebar: typing.Self,
param: GObject.ParamSpec) -> None:
model = None if self.model is None else self.model.treemodel
self._selection.props.model = model
class CalendarView(Gtk.Box):
"""Our calendar view for seleting an xfstests run by date."""
runid = GObject.Property(type=int)
sql = GObject.Property(type=GObject.TYPE_PYOBJECT)
def __init__(self, sql: sqlite.Connection) -> None:
"""Initialize a CalendarView instance."""
super().__init__(sql=sql, spacing=6,
orientation=Gtk.Orientation.VERTICAL)
today = datetime.date.today()
self._calendar = Gtk.Calendar()
self._view = RunidView(model=tree.DateDeviceList(sql, today))
self._view.bind_property("runid", self, "runid")
self._calendar.connect("day-selected", self.__day_selected)
self._calendar.connect("next-month", self.__date_changed)
self._calendar.connect("next-year", self.__date_changed)
self._calendar.connect("prev-month", self.__date_changed)
self._calendar.connect("prev-year", self.__date_changed)
self.__mark_days(today)
self.append(self._calendar)
self.append(self._view)
def __day_selected(self, calendar: Gtk.Calendar) -> None:
self.__select_day(datetime.date(*calendar.get_date().get_ymd()))
def __date_changed(self, calendar: Gtk.Calendar) -> None:
date = datetime.date(*calendar.get_date().get_ymd())
self.__mark_days(date)
self.__select_day(date)
def __select_day(self, date: datetime.date) -> None:
self._view.model = tree.DateDeviceList(self.sql, date)
def __mark_days(self, date: datetime.date) -> None:
min = datetime.datetime.combine(date.replace(day=1), datetime.time())
max = (min + datetime.timedelta(days=40)).replace(day=1)
self._calendar.clear_marks()
for stamp in self.sql("""SELECT DISTINCT timestamp FROM tagged_runs
WHERE timestamp >= ? AND timestamp < ?""",
min, max).fetchall():
ts = datetime.datetime.fromisoformat(stamp['timestamp'])
self._calendar.mark_day(ts.day)
class Sidebar(Gtk.Box):
"""Our sidebar for seleting an xfstests run."""
runid = GObject.Property(type=int)
sql = GObject.Property(type=GObject.TYPE_PYOBJECT)
def __init__(self, sql: sqlite.Connection) -> None:
"""Initialize a CalendarView instance."""
animation = Gtk.StackTransitionType.OVER_LEFT_RIGHT
super().__init__(sql=sql, orientation=Gtk.Orientation.VERTICAL)
self._stack = Gtk.Stack(transition_type=animation)
self._switcher = Gtk.StackSwitcher(stack=self._stack,
margin_top=6, margin_bottom=6,
margin_start=80, margin_end=80)
self._calendar = CalendarView(sql)
self._tags = RunidView(model=tree.TagList(sql))
self._calendar.bind_property("runid", self, "runid")
self._tags.bind_property("runid", self, "runid")
self.__add_page(self._calendar, "Calendar", "month-symbolic")
self.__add_page(self._tags, "Tags", "tag-symbolic")
self._switcher.add_css_class("large-icons")
self.append(self._switcher)
self.append(Gtk.Separator())
self.append(self._stack)
def __add_page(self, page: Gtk.Widget, title: str, icon: str) -> None:
pg = self._stack.add_titled(page, title.lower(), title)
pg.set_icon_name(icon)

View File

@ -1,224 +0,0 @@
# Copyright 2023 (c) Anna Schumaker.
"""Our tree model for selecting an xfstests run to view."""
import bisect
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):
"""A single XfstessRun."""
timestamp = GObject.Property(type=GObject.TYPE_PYOBJECT)
runid = GObject.Property(type=int)
ftime = GObject.Property(type=str)
def __init__(self, runid: int, timestamp: datetime.datetime,
ftime: str = "%T") -> None:
"""Initialize an XfstestsRun instance."""
super().__init__(timestamp=timestamp, runid=runid, ftime=ftime)
def __gt__(self, rhs: typing.Self) -> bool:
"""Compare the timestamps an runids of two XfstestRun objects."""
return (self.timestamp, self.runid) > (rhs.timestamp, rhs.runid)
def __lt__(self, rhs: typing.Self) -> bool:
"""Compare the timestamps and runids of two XfstestsRun objects."""
return (self.timestamp, self.runid) < (rhs.timestamp, rhs.runid)
def __str__(self) -> str:
"""Get a string representation of this run."""
return f"#{self.runid}: {self.timestamp.strftime(self.ftime)}"
class DeviceRunsList(GObject.GObject, Gio.ListModel):
"""Contains a single test device with multiple runs."""
name = GObject.Property(type=str)
n_items = GObject.Property(type=int)
def __init__(self, name: str) -> None:
"""Initialize our DeviceRunsList."""
super().__init__(name=name)
self.__runs = []
def __lt__(self, rhs: typing.Self) -> bool:
"""Compare two DeviceRunsList objects."""
return self.name < rhs.name
def __str__(self) -> str:
"""Get the name of this device."""
return self.name
def do_get_item_type(self) -> GObject.GType:
"""Get the type of objects in the list."""
return XfstestsRun.__gtype__
def do_get_n_items(self) -> int:
"""Get the number of runs added to this device."""
return self.n_items
def do_get_item(self, n: int) -> XfstestsRun:
"""Get the run at the nth position."""
return self.__runs[n]
def add_run(self, runid: int, timestamp: datetime.datetime,
ftime: str = "%T") -> None:
"""Add a run to the Device's list."""
bisect.insort(self.__runs, XfstestsRun(runid, timestamp, ftime))
self.n_items = len(self.__runs)
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)
class TagDeviceList(GObject.GObject, Gio.ListModel):
"""Contains a single tag with multiple devices and runs."""
name = GObject.Property(type=str)
n_items = GObject.Property(type=int)
def __init__(self, name: str) -> None:
"""Initialize our TagDeviceList."""
super().__init__(name=name)
self.__devices = []
def __lt__(self, rhs: typing.Self) -> bool:
"""Compare two TagDeviceList objects."""
lhs_run = self.get_earliest_run()
rhs_run = rhs.get_earliest_run()
if lhs_run.runid == rhs_run.runid:
return self.name > rhs.name
return lhs_run > rhs_run
def __str__(self) -> str:
"""Get the name of this tag."""
return self.name
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 devices added to this tag."""
return self.n_items
def do_get_item(self, n: int) -> DeviceRunsList:
"""Get the device at the nth position."""
return self.__devices[n]
def add_run(self, runid: int, device: str,
timestamp: datetime.datetime) -> None:
"""Add a run to the Tag's list."""
pos = bisect.bisect_left(self.__devices, device, key=str)
if pos < len(self.__devices) and self.__devices[pos].name == device:
dev = self.__devices[pos]
else:
dev = DeviceRunsList(device)
self.__devices.insert(pos, dev)
dev.add_run(runid, timestamp, "%c")
self.n_items = len(self.__devices)
def get_earliest_run(self) -> XfstestsRun | None:
"""Get the earliest run added to the tag."""
runs = [d.get_earliest_run() for d in self.__devices]
return min(runs, default=None)
class TagList(GObject.GObject, Gio.ListModel):
"""A list containing all tagged xfstests runs."""
n_items = GObject.Property(type=int)
treemodel = GObject.Property(type=Gtk.TreeListModel)
def __init__(self, sql: sqlite.Connection) -> None:
"""Initialize our TagList."""
super().__init__()
tags = {}
for row in sql("""SELECT DISTINCT runid, device, timestamp, tag
FROM tagged_runs WHERE tag IS NOT NULL""").fetchall():
if (tag := tags.get(row['tag'])) is None:
tags[row['tag']] = tag = TagDeviceList(row['tag'])
ts = datetime.datetime.fromisoformat(row['timestamp'])
tag.add_run(row['runid'], row['device'], ts)
self.__items = sorted(tags.values())
self.n_items = len(self.__items)
self.treemodel = Gtk.TreeListModel.new(root=self, passthrough=False,
autoexpand=False,
create_func=self.__create_func)
def __create_func(self, item: GObject.GObject) -> Gio.ListModel:
if isinstance(item, TagDeviceList | DeviceRunsList):
return item
return None
def do_get_item_type(self) -> GObject.GType:
"""Get the type of objects in the list."""
return TagDeviceList.__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) -> TagDeviceList:
"""Get a specific item in the list."""
return self.__items[n]

View File

@ -33,8 +33,6 @@ class XunitView(Gtk.ScrolledWindow):
hexpand=True),
**kwargs)
self.props.child.get_model().set_model(self.filtermodel)
self.add_css_class("undershoot-top")
self.add_css_class("undershoot-bottom")
self.add_css_class("card")
def do_make_factory(self, xunit: str) -> row.XunitFactory:
@ -244,11 +242,11 @@ class MessagesView(Gtk.Box):
def __init__(self):
"""Initialize a MessagesView."""
icon = "down-large-symbolic"
icon = "go-previous-symbolic"
super().__init__(orientation=Gtk.Orientation.VERTICAL, margin_top=24,
margin_start=24, margin_end=24, margin_bottom=24)
self._back = Gtk.Button(child=Adw.ButtonContent(icon_name=icon,
label="done"))
label="back"))
self._title = Adw.WindowTitle()
self._stdout = MessageView("stdout")
self._stderr = MessageView("stderr")
@ -301,7 +299,7 @@ class XfstestsView(Gtk.Box):
def __init__(self):
"""Initialize an XfstestsView."""
animation = Gtk.StackTransitionType.OVER_UP_DOWN
animation = Gtk.StackTransitionType.OVER_LEFT_RIGHT
super().__init__(orientation=Gtk.Orientation.VERTICAL)
self._environview = EnvironmentView()
self._propertyview = PropertyView()

View File

@ -10,11 +10,9 @@ class Window(Adw.Window):
"""Our customized Window displayed to the user."""
child = GObject.Property(type=Gtk.Widget)
sidebar = GObject.Property(type=Gtk.Widget)
headerbar = GObject.Property(type=Adw.HeaderBar)
title = GObject.Property(type=Adw.WindowTitle)
runid = GObject.Property(type=int)
show_sidebar = GObject.Property(type=bool, default=False)
def __init__(self, **kwargs):
"""Set up our Window."""
@ -22,26 +20,13 @@ class Window(Adw.Window):
content=Gtk.Box(orientation=Gtk.Orientation.VERTICAL),
title=Adw.WindowTitle(title="xfstestsdb gtk"),
headerbar=Adw.HeaderBar(), **kwargs)
self._splitview = Adw.OverlaySplitView(content=self.child,
sidebar=self.sidebar,
collapsed=True,
show_sidebar=self.show_sidebar)
self._show_sidebar = Gtk.ToggleButton(icon_name="sidebar-show",
active=self.show_sidebar)
self.headerbar.pack_start(self._show_sidebar)
self.headerbar.props.title_widget = self.title
self.headerbar.add_css_class("flat")
self.props.content.append(self.headerbar)
self.props.content.append(self._splitview)
self._show_sidebar.bind_property("active", self, "show-sidebar",
GObject.BindingFlags.BIDIRECTIONAL)
self.bind_property("show-sidebar", self._splitview, "show-sidebar")
self.bind_property("sidebar", self._splitview, "sidebar")
self.bind_property("child", self._splitview, "content")
self.props.content.append(Adw.Bin(child=self.child))
self.bind_property("child", self.headerbar.get_next_sibling(), "child")
self.connect("notify::runid", self.__notify_runid)
if __debug__: