222 lines
8.1 KiB
Python
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)
|