diff --git a/report-xfstests.py b/report-xfstests.py index c94183e..bc93d69 100755 --- a/report-xfstests.py +++ b/report-xfstests.py @@ -15,42 +15,6 @@ from gi.repository import Gio XFSTESTS = pathlib.Path(xdg.BaseDirectory.xdg_data_home) / "xfstests" / "date" -class TestProperty(): - def __init__(self, name): - self.name = name - self.prop = dict() - - def __lt__(self, rhs): - return self.name.upper() < rhs.name.upper() - - def __getitem__(self, key): - return self.prop[key] - - def __setitem__(self, key, val): - self.prop[key] = val - - -class PropertyPage(Gtk.ScrolledWindow): - def __init__(self, name, results): - Gtk.ScrolledWindow.__init__(self, vscrollbar_policy=Gtk.PolicyType.NEVER) - self.set_child(Gtk.Grid()) - - res = { p.name : p[name] for p in results.properties } - passed = int(res['tests']) - (int(res['skipped']) + int(res['failures'])) - res["RESULTS"] = f"Ran {res['tests']} tests in {res['time']} seconds: " \ - f"{passed} passed, {res['failures']} failed, {res['skipped']} skipped" - - fields = [ "PLATFORM", "timestamp", "FSTYP", "MOUNT_OPTIONS", "CHECK_OPTIONS", - "TEST_DIR", "TEST_DEV", "SCRATCH_DEV", "SCRATCH_MNT", "RESULTS" ] - for i, field in enumerate(fields): - key = Gtk.Label.new(field.upper() + " = ") - key.set_xalign(100) - val = Gtk.Label.new(res[field]) - val.set_xalign(0) - self.get_child().get_child().attach(key, 0, i, 1, 1) - self.get_child().get_child().attach(val, 1, i, 1, 1) - - class TestCase(GObject.GObject): def __init__(self, name): GObject.GObject.__init__(self) @@ -88,7 +52,6 @@ class TestCase(GObject.GObject): class TestResultsModel(GObject.GObject, Gio.ListModel): def __init__(self): GObject.GObject.__init__(self) - self.properties = [ ] self.tests = [ ] self.columns = [ "Test Name" ] @@ -98,13 +61,6 @@ class TestResultsModel(GObject.GObject, Gio.ListModel): def get_n_columns(self): return len(self.columns) def get_column_name(self, n): return self.columns[n] - def find_property(self, name): - for prop in self.properties: - if prop.name == name: - return prop - self.properties.append(TestProperty(name)) - return self.properties[-1] - def find_testcase(self, name): i = bisect.bisect(self.tests, name, key=str) if i > 0 and self.tests[i-1].name == name: @@ -115,24 +71,16 @@ class TestResultsModel(GObject.GObject, Gio.ListModel): def set_tests(self, path): rm = len(self.tests) self.columns = [ "Test Name" ] - self.properties.clear() self.tests.clear() for file in sorted(path.iterdir() if path else []): self.columns.append(file.stem) root = xml.etree.ElementTree.parse(file).getroot() - for prop in root.attrib.keys(): - self.find_property(prop)[file.stem] = root.attrib[prop] for elm in root: if elm.tag == "testcase": testcase = self.find_testcase(elm.attrib["name"]) testcase[file.stem] = elm - elif elm.tag == "properties": - for prop in elm: - name = prop.attrib["name"] - value = prop.attrib["value"] - self.find_property(name)[file.stem] = value self.emit("items-changed", 0, rm, len(self.tests)) @@ -224,7 +172,6 @@ class TestWindow(Gtk.ScrolledWindow): self.filter = Gtk.FilterListModel.new(self.results, TestResultsFilter()) self.selection = Gtk.NoSelection.new(self.filter) self.view = Gtk.ColumnView.new(self.selection) - self.notebook = Gtk.Notebook() self.set_child(self.view) if len(sys.argv) > 1: self.set_tests(pathlib.Path(sys.argv[1])) @@ -241,23 +188,19 @@ class TestWindow(Gtk.ScrolledWindow): def set_tests(self, path): for column in [ c for c in self.view.get_columns() ]: self.view.remove_column(column) - for n in range(self.notebook.get_n_pages()): - self.notebook.remove_page(0) self.results.set_tests(path) for n in range(self.results.get_n_columns()): name = self.results.get_column_name(n) col = Gtk.ColumnViewColumn.new(name, TestResultsFactory(name)) col.set_expand(n > 0) self.view.append_column(col) - if n > 0: - self.notebook.append_page(PropertyPage(name, self.results), - Gtk.Label.new(name)) class Window(Gtk.Window): def __init__(self): Gtk.Window.__init__(self, title="Xfstests Results") self.chooser = reporter.TestChooser() + self.testview = reporter.TestView() self.passing = Gtk.Switch(halign=Gtk.Align.CENTER, active=True) self.lpass = Gtk.Label.new("Show Passing Tests") self.skipped = Gtk.Switch(halign=Gtk.Align.CENTER, active=True) @@ -274,7 +217,7 @@ class Window(Gtk.Window): self.left.attach(self.lskip, 1, 2, 1, 1) self.right = Gtk.Box.new(Gtk.Orientation.VERTICAL, 5) - self.right.append(self.xfstests.notebook) + self.right.append(self.testview) self.right.append(self.search) self.right.append(self.xfstests) @@ -282,8 +225,6 @@ class Window(Gtk.Window): self.child.append(self.left) self.child.append(self.right) - reporter.common.SizeGroup.add_widget(self.xfstests.notebook) - self.lskip.set_xalign(0) self.lpass.set_xalign(0) self.set_default_size(1100, 750) @@ -295,9 +236,8 @@ class Window(Gtk.Window): self.search.connect("search-changed", self.search_changed) def test_changed(self, window, file): + self.testview.set_test_result(file) self.xfstests.set_tests(file.path if file else None) - if file and file.get_is_test_result(): - results = reporter.testresults.TestResults(file.path) def show_passing(self, passing, param): self.xfstests.set_show_passing(passing.get_active()) diff --git a/reporter/__init__.py b/reporter/__init__.py index ee7ef1d..4b183e9 100644 --- a/reporter/__init__.py +++ b/reporter/__init__.py @@ -1,6 +1,7 @@ #!/usr/bin/python from . import common from . import testchooser +from . import testproperties from . import testresults from gi.repository import GObject from gi.repository import Gtk @@ -18,3 +19,17 @@ class TestChooser(Gtk.Box): @GObject.Signal(arg_types=(testchooser.Path,)) def test_selected(self, path): pass + + +class TestView(Gtk.Box): + def __init__(self): + Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL) + self.stack = testproperties.Stack() + self.append(Gtk.StackSwitcher(stack=self.stack, halign=Gtk.Align.CENTER)) + self.append(self.stack) + + def set_test_result(self, file): + self.stack.clear() + if file and file.get_is_test_result(): + results = testresults.TestResults(file.path) + self.stack.show_properties(results) diff --git a/reporter/testproperties.py b/reporter/testproperties.py new file mode 100644 index 0000000..0ed5905 --- /dev/null +++ b/reporter/testproperties.py @@ -0,0 +1,90 @@ +#!/usr/bin/python +from . import common +from gi.repository import GObject +from gi.repository import Gio +from gi.repository import Gtk + +PROPERTIES = [ "PLATFORM", "TIMESTAMP", "FSTYP", "MOUNT_OPTIONS", "CHECK_OPTIONS", + "TEST_DIR", "TEST_DEV", "SCRATCH_DEV", "SCRATCH_MNT" ] #, "RESULTS" ] + +class Property(GObject.GObject): + def __init__(self, key, value): + GObject.GObject.__init__(self) + self.key = key + self.value = value + + +class Results(Property): + def __init__(self, properties): + total = properties["TESTS"] + time = properties["TIME"] + failed = properties["FAILURES"] + skipped = properties["SKIPPED"] + passed = int(total) - (int(skipped) + int(failed)) + Property.__init__(self, "RESULTS", + f"Ran {total} tests in {time} seconds: " \ + f"{passed} passed, {failed} failed, {skipped} skipped") + + +class Model(GObject.GObject, Gio.ListModel): + def __init__(self, properties): + GObject.GObject.__init__(self) + self.properties = [ Property(p, properties[p]) for p in PROPERTIES ] + self.properties.append(Results(properties)) + + def do_get_item_type(self): return GObject.TYPE_PYOBJECT + def do_get_n_items(self): return len(self.properties) + def do_get_item(self, i): return self.properties[i] + + +class Factory(Gtk.SignalListItemFactory): + def __init__(self, column): + Gtk.SignalListItemFactory.__init__(self) + self.column = column + self.connect("setup", self.on_setup) + self.connect("bind", self.on_bind) + self.connect("unbind", self.on_unbind) + self.connect("teardown", self.on_teardown) + + def on_setup(self, factory, listitem): + listitem.set_child(Gtk.Label(xalign=0)) + + def on_bind(self, factory, listitem): + label = listitem.get_child() + match self.column: + case "Property": label.set_text(listitem.get_item().key) + case "Value": label.set_text(listitem.get_item().value) + case _: label.set_text("=") + + def on_unbind(self, factory, listitem): + listitem.get_child().set_text("") + + def on_teardown(self, factory, listitem): + listitem.set_child(None) + + +class View(Gtk.ColumnView): + def __init__(self, properties): + self.selection = Gtk.NoSelection.new(Model(properties)) + Gtk.ColumnView.__init__(self, model=self.selection) + self.add_css_class("data-table") + for title in [ "Property", "=", "Value" ]: + self.append_column(Gtk.ColumnViewColumn.new(title, Factory(title))) + + +class Stack(Gtk.Stack): + def __init__(self): + Gtk.Stack.__init__(self, transition_type=Gtk.StackTransitionType.OVER_LEFT_RIGHT) + common.SizeGroup.add_widget(self) + + def clear(self): + pages = self.get_pages() + children = [ pages.get_item(i).get_child() for i in range(pages.get_n_items()) ] + for child in children: + self.remove(child) + + def show_properties(self, results): + for version in results.versions: + window = Gtk.ScrolledWindow(vscrollbar_policy=Gtk.PolicyType.NEVER, + child=View(results.properties[version])) + self.add_titled(window, version, version) diff --git a/reporter/testresults.py b/reporter/testresults.py index 8a3eb43..c0a11f7 100644 --- a/reporter/testresults.py +++ b/reporter/testresults.py @@ -61,7 +61,7 @@ class TestResults: self.add_testcase(elm.attrib["name"], file.stem, result) def set_property(self, name, vers, value): - self.properties.setdefault(name.upper(), dict())[vers] = value + self.properties.setdefault(vers, dict())[name.upper()] = value def add_testcase(self, name, vers, result): self.tests.setdefault(name, TestCase(name))[vers] = result