# 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)