xfstestsdb/xfstestsdb/gtk/row.py

222 lines
8.1 KiB
Python

# Copyright 2023 (c) Anna Schumaker.
"""Our TestCase row widgets and factory."""
import typing
from gi.repository import GObject
from gi.repository import Gtk
from . import model
from . import tree
STYLES = {"passed": "success", "failed": "error",
"skipped": "warning", "time": "accent"}
class Factory(Gtk.SignalListItemFactory):
"""Create Gtk.Inscriptions for each Gtk.ListItem."""
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.Inscription(xalign=0.5, nat_chars=18)
child.props.text_overflow = Gtk.InscriptionOverflow.ELLIPSIZE_END
child.add_css_class("numeric")
self.do_setup(child)
listitem.set_child(child)
def __bind(self, factory: typing.Self, listitem: Gtk.ListItem) -> None:
"""Bind a ListItem to the child widget."""
self.do_bind(listitem.get_item(), listitem.get_child())
def __unbind(self, factory: typing.Self, listitem: Gtk.ListItem) -> None:
"""Unbind a ListItem from the child widget."""
self.do_unbind(listitem.get_item(), listitem.get_child())
listitem.get_child().set_text(None)
def __teardown(self, factory: typing.Self, listitem: Gtk.ListItem) -> None:
self.do_teardown(listitem.get_child())
listitem.set_child(None)
def do_setup(self, child: Gtk.Inscription) -> None:
"""Extra factory-specific setup for the child widget."""
def do_bind(self, row: model.XunitRow, child: Gtk.Inscription) -> None:
"""Extra factory-specific binding work for the child widget."""
def do_unbind(self, row: model.XunitRow, child: Gtk.Inscription) -> None:
"""Extra factory-specific unbinding work for the child widget."""
def do_teardown(self, child: Gtk.Inscription) -> None:
"""Extra factory-specific teardown for the child widget."""
class LabelFactory(Factory):
"""Create Gtk.Labels for each testcase."""
property = GObject.Property(type=str)
group = Gtk.SizeGroup()
def __init__(self, property: str):
"""Initialize our InscriptionFactory."""
super().__init__(property=property)
def do_setup(self, child: Gtk.Inscription) -> None:
"""Set up a ListItem child widget."""
LabelFactory.group.add_widget(child)
def do_bind(self, row: model.XunitRow, child: Gtk.Inscription) -> None:
"""Bind a ListItem to the child widget."""
text = row.get_property(self.property)
if style := STYLES.get(text):
child.add_css_class(style)
child.set_text(text)
def do_unbind(self, row: model.XunitRow, child: Gtk.Inscription) -> None:
"""Unbind a ListItem from the child widget."""
for style in STYLES.values():
child.remove_css_class(style)
def do_teardown(self, child: Gtk.Inscription) -> None:
"""Clean up a ListItem child widget."""
if child is not None:
LabelFactory.group.remove_widget(child)
class EnvironmentFactory(Factory):
"""Factory for Environment property columns."""
property = GObject.Property(type=str)
def __init__(self, *, property: str):
"""Initialize our Environment Factory."""
super().__init__(property=property)
def do_bind(self, row: model.PropertyList, child: Gtk.Inscription) -> None:
"""Bind an Environment property to the child widget."""
text = row.get_environment()[self.property]
child.set_xalign(0)
child.set_text(text)
child.set_tooltip_text(text)
class XunitFactory(Factory):
"""Factory base class for Xunit columns."""
xunit = GObject.Property(type=str)
def __init__(self, *, xunit: str):
"""Initialize our Xunit Factory."""
super().__init__(xunit=xunit)
class PropertyFactory(XunitFactory):
"""Factory for making property widgets."""
def do_bind(self, row: model.TestCase, child: Gtk.Inscription) -> None:
"""Bind a ListItem to the child widget."""
property = row[self.xunit]
child.set_text(property.value)
child.set_tooltip_text(property.value)
class ResultFactory(XunitFactory):
"""Factory for making test result widgets."""
def __clicked(self, click: Gtk.GestureClick, n_press:
int, x: float, y: float, row: model.TestCase) -> None:
if (result := row[self.xunit]) is not None:
if len(result.stdout) > 0 or len(result.stderr) > 0:
self.emit("show-messages", row.name, self.xunit,
result.stdout, result.stderr)
def do_setup(self, child: Gtk.Inscription) -> None:
"""Set up click handling on the child widget."""
child.click = Gtk.GestureClick()
child.add_controller(child.click)
def do_bind(self, row: model.TestCase, child: Gtk.Inscription) -> None:
"""Bind a ListItem to the child widget."""
if (result := row[self.xunit]) is None:
return
if (text := result.status) == "passed":
text = f"{result.time} seconds"
child.set_text(text)
child.set_tooltip_text(result.message.lstrip(" -"))
child.get_parent().add_css_class(result.status)
child.click.connect("released", self.__clicked, row)
def do_unbind(self, row: model.TestCase, child: Gtk.Inscription) -> None:
"""Unbind a ListItem from the child widget."""
if (result := row[self.xunit]) is not None:
child.get_parent().remove_css_class(result.status)
child.click.disconnect_by_func(self.__clicked)
def do_teardown(self, child: Gtk.Inscription) -> None:
"""Clean up the GestureClick."""
child.remove_controller(child.click)
setattr(child, "click", None)
@GObject.Signal(arg_types=(str, str, str, str))
def show_messages(self, testcase: str, xunit: str,
stdout: str, stderr: str) -> None:
"""Show the selected messages to the user."""
class SummaryFactory(XunitFactory):
"""Factory for making test summary widgets."""
def do_bind(self, row: model.Summary, child: Gtk.Inscription) -> None:
"""Bind a ListItem to the child widget."""
result = row[self.xunit]
child.set_text(str(result))
child.add_css_class(STYLES[row.name])
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)