xfstestsdb/xfstestsdb/gtk/view.py

213 lines
7.9 KiB
Python

# Copyright 2023 (c) Anna Schumaker.
"""A view widget used to display our TestCaseModel."""
from gi.repository import GObject
from gi.repository import Gio
from gi.repository import Gtk
from .model import PropertyList
from .model import PropertyFilter
from .model import TestCaseList
from .model import TestCaseFilter
from .model import SummaryList
from .model import XunitList
from . import button
from . import row
class XunitView(Gtk.ScrolledWindow):
"""Our XunitView base class."""
filtermodel = GObject.Property(type=Gtk.FilterListModel)
firstcol = GObject.Property(type=Gtk.ColumnViewColumn)
def __init__(self, title: str, **kwargs):
"""Initialize an XunitView."""
factory = row.LabelFactory("name")
super().__init__(filtermodel=Gtk.FilterListModel(),
firstcol=Gtk.ColumnViewColumn(title=title,
factory=factory),
child=Gtk.ColumnView(model=Gtk.NoSelection(),
show_row_separators=True,
show_column_separators=True,
hexpand=True),
**kwargs)
self.props.child.get_model().set_model(self.filtermodel)
self.add_css_class("card")
def do_make_factory(self, xunit: str) -> row.XunitFactory:
"""Make an XunitFactory for the given xunit."""
return row.XunitFactory(xunit=xunit)
@GObject.Property(type=XunitList)
def model(self) -> XunitList | None:
"""Get the XunitList shown by the XunitView."""
return self.filtermodel.props.model
@model.setter
def model(self, new: XunitList) -> None:
for col in list(self.props.child.get_columns()):
self.props.child.remove_column(col)
self.filtermodel.props.model = new
if new is not None:
self.props.child.append_column(self.firstcol)
for xunit in new.get_xunits():
col = Gtk.ColumnViewColumn(title=xunit, expand=True,
factory=self.do_make_factory(xunit))
self.props.child.append_column(col)
class EnvironmentView(XunitView):
"""Displays our Environment properties to the user."""
def __init__(self):
"""Initialize an EnvironmentView."""
super().__init__("environment", visible=False)
self.firstcol = None
self.props.child.add_css_class("data-table")
def do_make_factory(self, property: str) -> row.EnvironmentFactory:
"""Make a new EnvironmentFactory instance."""
return row.EnvironmentFactory(property=property)
@GObject.Property(type=Gio.ListModel)
def model(self) -> Gio.ListModel | None:
"""Get the ListModel shown by the EnvironmentView."""
return self.filtermodel.props.model
@model.setter
def model(self, new: Gio.ListModel) -> None:
for col in list(self.props.child.get_columns()):
self.props.child.remove_column(col)
self.filtermodel.props.model = new
self.props.visible = new is not None
if new is not None:
environ = new[0].get_environment()
for prop in environ.keys():
col = Gtk.ColumnViewColumn(title=prop, expand=True,
factory=self.do_make_factory(prop))
self.props.child.append_column(col)
class PropertyView(XunitView):
"""Displays our PropertyList model to the user."""
def __init__(self):
"""Initialize a PropertyView."""
super().__init__("property", vscrollbar_policy=Gtk.PolicyType.NEVER)
self.filtermodel.set_filter(PropertyFilter())
self.props.child.add_css_class("data-table")
def do_make_factory(self, xunit: str) -> row.PropertyFactory:
"""Make a new PropertyFactory instance."""
return row.PropertyFactory(xunit=xunit)
class FilterButtons(Gtk.Box):
"""Buttons for controlling the TestCaseFilter."""
passed = GObject.Property(type=bool, default=False)
skipped = GObject.Property(type=bool, default=False)
failure = GObject.Property(type=bool, default=True)
def __init__(self):
"""Initialize the FilterButtons."""
super().__init__()
self.add_css_class("linked")
self._passed = button.StatusToggle("test-pass", "passed")
self._skipped = button.StatusToggle("test-skip", "skipped")
self._failure = button.StatusToggle("test-fail", "failure",
active=True)
self._passed.bind_property("active", self, "passed")
self._skipped.bind_property("active", self, "skipped")
self._failure.bind_property("active", self, "failure")
self.append(self._passed)
self.append(self._skipped)
self.append(self._failure)
class TestCaseView(XunitView):
"""Displays our TestCaseList model to the user."""
filterbuttons = GObject.Property(type=FilterButtons)
def __init__(self):
"""Initialize a TestCaseView."""
super().__init__("testcase", filterbuttons=FilterButtons())
self.filtermodel.props.filter = TestCaseFilter()
for prop in ["passed", "skipped", "failure"]:
self.filterbuttons.bind_property(prop,
self.filtermodel.props.filter,
prop)
self.props.child.set_vexpand(True)
def __show_messages(self, factory: row.ResultFactory, testcase: str,
xunit: str, stdout: str, stderr: str) -> None:
self.emit("show-messages", testcase, xunit, stdout, stderr)
def do_make_factory(self, xunit: str) -> row.ResultFactory:
"""Make a new ResultFactory instance."""
factory = row.ResultFactory(xunit=xunit)
factory.connect("show-messages", self.__show_messages)
return factory
@GObject.Signal(arg_types=(str, str, str, str))
def show_messages(self, testcase: str, xunit: str,
stdout: str, stderr: str) -> None:
"""Signal that the user wants to inspect stdout and stderr messages."""
class SummaryView(XunitView):
"""Displays our SummaryList model to the user."""
def __init__(self):
"""Initialize a SummaryView."""
super().__init__("summary", vscrollbar_policy=Gtk.PolicyType.NEVER)
self.props.child.add_css_class("data-table")
def do_make_factory(self, xunit: str) -> row.SummaryFactory:
"""Make a new SummaryFactory instance."""
return row.SummaryFactory(xunit=xunit)
class XfstestsView(Gtk.Box):
"""A widget to display the results of an Xfstests runs."""
environment = GObject.Property(type=Gio.ListModel)
properties = GObject.Property(type=PropertyList)
model = GObject.Property(type=TestCaseList)
summary = GObject.Property(type=SummaryList)
def __init__(self):
"""Initialize an XfstestsView."""
super().__init__(orientation=Gtk.Orientation.VERTICAL)
self._environview = EnvironmentView()
self._propertyview = PropertyView()
self._testcaseview = TestCaseView()
self._summaryview = SummaryView()
self.bind_property("environment", self._environview, "model")
self.bind_property("properties", self._propertyview, "model")
self.bind_property("model", self._testcaseview, "model")
self.bind_property("summary", self._summaryview, "model")
self.append(self._environview)
self.append(Gtk.Separator())
self.append(self._propertyview)
self.append(Gtk.Separator())
self.append(self._testcaseview)
self.append(Gtk.Separator())
self.append(self._summaryview)
@GObject.Property(type=FilterButtons)
def filterbuttons(self) -> FilterButtons:
"""Get the FilterButtons attached to the child TestCaseView."""
return self._testcaseview.filterbuttons