From 7adbb5938d1afec7188c3ac0786f599a00727960 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Tue, 1 Aug 2023 13:16:26 -0400 Subject: [PATCH] gtk: Add a TestCaseList Gio.ListModel This is a list model designed to show xfstests results for a given runid. I create a new one whenever the Application changes the runid. Signed-off-by: Anna Schumaker --- tests/gtk/test_model.py | 54 ++++++++++++++++++++++++++++++++++++++ tests/test_gtk.py | 10 +++++-- xfstestsdb/gtk/__init__.py | 14 +++++++--- xfstestsdb/gtk/model.py | 44 +++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 6 deletions(-) diff --git a/tests/gtk/test_model.py b/tests/gtk/test_model.py index 359356f..9403009 100644 --- a/tests/gtk/test_model.py +++ b/tests/gtk/test_model.py @@ -1,8 +1,10 @@ # Copyright 2023 (c) Anna Schumaker. """Tests our Testcase Gio.ListModel.""" import unittest +import tests.xunit import xfstestsdb.gtk.model from gi.repository import GObject +from gi.repository import Gio class TestXunitResult(unittest.TestCase): @@ -67,3 +69,55 @@ class TestTestCase(unittest.TestCase): self.assertEqual(xunit.message, "") self.assertEqual(xunit.stdout, "") self.assertEqual(xunit.stderr, "") + + +class TestCaseList(unittest.TestCase): + """Tests our TestCaseList Gio.ListModel.""" + + def setUp(self): + """Set up common variables.""" + self.xfstestsdb = xfstestsdb.Command() + with unittest.mock.patch("sys.stdout"): + self.xfstestsdb.run(["new", "/dev/vda1"]) + self.xfstestsdb.run(["xunit", "read", "--name", "xunit-1", + "1", str(tests.xunit.XUNIT_1)]) + self.xfstestsdb.run(["xunit", "read", "--name", "xunit-2", + "1", str(tests.xunit.XUNIT_1)]) + + self.tclist = xfstestsdb.gtk.model.TestCaseList(self.xfstestsdb.sql, 1) + + def test_init(self): + """Test that the TestCaseList was set up properly.""" + self.assertIsInstance(self.tclist, GObject.GObject) + self.assertIsInstance(self.tclist, Gio.ListModel) + self.assertEqual(self.tclist.runid, 1) + + def test_get_item_type(self): + """Test the get_item_type() function.""" + self.assertEqual(self.tclist.get_item_type(), + xfstestsdb.gtk.model.TestCase.__gtype__) + + def test_get_n_items(self): + """Test the get_n_items() function.""" + self.assertEqual(self.tclist.get_n_items(), 10) + self.assertEqual(self.tclist.n_items, 10) + + def test_get_item(self): + """Test the get_item() function.""" + testcase = self.tclist.get_item(0) + self.assertIsInstance(testcase, xfstestsdb.gtk.model.TestCase) + self.assertEqual(testcase.name, "test/01") + for xunit in ["xunit-1", "xunit-2"]: + with self.subTest(xunit=xunit): + self.assertEqual(testcase[xunit].name, xunit) + self.assertEqual(testcase[xunit].status, "passed") + self.assertEqual(testcase[xunit].time, 1) + self.assertEqual(testcase[xunit].message, "") + self.assertEqual(testcase[xunit].stdout, "") + self.assertEqual(testcase[xunit].stderr, "") + + self.assertIsNone(self.tclist.get_item(10)) + + def test_get_xunits(self): + """Test the get_xunits() function.""" + self.assertListEqual(self.tclist.get_xunits(), ["xunit-1", "xunit-2"]) diff --git a/tests/test_gtk.py b/tests/test_gtk.py index dc991a4..b70a176 100644 --- a/tests/test_gtk.py +++ b/tests/test_gtk.py @@ -14,7 +14,8 @@ class TestApplication(unittest.TestCase): def setUp(self): """Set up common variables.""" - self.application = xfstestsdb.gtk.Application() + self.xfstestsdb = xfstestsdb.Command() + self.application = xfstestsdb.gtk.Application(self.xfstestsdb.sql) def test_init(self): """Check that the Gtk Application was set up properly.""" @@ -24,6 +25,7 @@ class TestApplication(unittest.TestCase): self.assertEqual(self.application.get_flags(), Gio.ApplicationFlags.HANDLES_COMMAND_LINE) self.assertEqual(self.application.runid, 0) + self.assertIsNone(self.application.model) @unittest.mock.patch("gi.repository.Adw.Application.activate") @unittest.mock.patch("gi.repository.Adw.Application.do_command_line") @@ -38,6 +40,7 @@ class TestApplication(unittest.TestCase): mock_cmd.get_arguments.assert_called() mock_activate.assert_called() self.assertEqual(self.application.runid, 0) + self.assertIsNone(self.application.model) mock_command_line.reset_mock() mock_activate.reset_mock() @@ -49,6 +52,9 @@ class TestApplication(unittest.TestCase): mock_cmd.get_arguments.assert_called() mock_activate.assert_called() self.assertEqual(self.application.runid, 42) + self.assertIsInstance(self.application.model, + xfstestsdb.gtk.model.TestCaseList) + self.assertEqual(self.application.model.runid, 42) @unittest.mock.patch("gi.repository.Adw.Application.add_window") @unittest.mock.patch("gi.repository.Adw.Application.do_startup") @@ -121,7 +127,7 @@ class TestGtk(unittest.TestCase): """Check running `xfstestsdb gtk` with the --runid option.""" self.xfstestsdb.run(["new", "/dev/vda1"]) self.xfstestsdb.run(["gtk", "1"]) - mock_app.assert_called() + mock_app.assert_called_with(self.xfstestsdb.sql) mock_app.return_value.run.assert_called_with(["runid=1"]) @unittest.mock.patch("sys.stderr", new_callable=io.StringIO) diff --git a/xfstestsdb/gtk/__init__.py b/xfstestsdb/gtk/__init__.py index 9d07b13..307c18e 100644 --- a/xfstestsdb/gtk/__init__.py +++ b/xfstestsdb/gtk/__init__.py @@ -7,6 +7,7 @@ from . import gsetup from gi.repository import GObject from gi.repository import Gio from gi.repository import Adw +from . import model from . import window from .. import commands from .. import sqlite @@ -16,12 +17,15 @@ class Application(Adw.Application): """Our Adw.Application for displaying xfstests results.""" runid = GObject.Property(type=int) + model = GObject.Property(type=model.TestCaseList) win = GObject.Property(type=window.Window) + sql = GObject.Property(type=GObject.TYPE_PYOBJECT) - def __init__(self): + def __init__(self, sql: sqlite.Connection): """Initialize the application.""" super().__init__(application_id=gsetup.APPLICATION_ID, - flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE) + flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE, + sql=sql) def do_command_line(self, cmd_line: Gio.ApplicationCommandLine) -> int: """Handle the Adw.Application::command-line signal.""" @@ -30,7 +34,9 @@ class Application(Adw.Application): for arg in cmd_line.get_arguments(): split = arg.split("=") match split[0]: - case "runid": self.runid = int(split[1]) + case "runid": + self.runid = int(split[1]) + self.model = model.TestCaseList(self.sql, self.runid) self.activate() return 0 @@ -74,4 +80,4 @@ class Command(commands.Command): file=sys.stderr) sys.exit(errno.ENOENT) - Application().run(app_args) + Application(self.sql).run(app_args) diff --git a/xfstestsdb/gtk/model.py b/xfstestsdb/gtk/model.py index 79f6645..9726b8b 100644 --- a/xfstestsdb/gtk/model.py +++ b/xfstestsdb/gtk/model.py @@ -2,6 +2,8 @@ """Our Testcase Gio.ListModel.""" import typing from gi.repository import GObject +from gi.repository import Gio +from .. import sqlite class XunitResult(GObject.GObject): @@ -43,3 +45,45 @@ class TestCase(GObject.GObject): self.__xunits[name] = XunitResult(name=name, status=status, time=time, message=message, stdout=stdout, stderr=stderr) + + +class TestCaseList(GObject.GObject, Gio.ListModel): + """A list of TestCases for a specific Xfstests Run.""" + + runid = GObject.Property(type=int) + n_items = GObject.Property(type=int) + + def __init__(self, sql: sqlite.Connection, runid: int) -> None: + """Initialize a TestCaseList.""" + super().__init__(runid=runid) + self.__xunits = set() + + cases = {} + cur = sql("""SELECT testcase, xunit, status, + time, message, stdout, stderr + FROM testcases_view WHERE runid=?""", runid) + for row in cur.fetchall(): + testcase = cases.setdefault(row["testcase"], + TestCase(row["testcase"])) + testcase.add_xunit(row["xunit"], row["status"], row["time"], + row["message"], row["stdout"], row["stderr"]) + self.__xunits.add(row["xunit"]) + + self.__items = sorted(cases.values()) + self.n_items = len(self.__items) + + def do_get_item_type(self) -> GObject.GType: + """Get the type of the objects in the list.""" + return TestCase.__gtype__ + + def do_get_n_items(self) -> int: + """Get the number of items in the list.""" + return self.n_items + + def do_get_item(self, n: int) -> TestCase | None: + """Get a specific item on the list.""" + return self.__items[n] if n < self.n_items else None + + def get_xunits(self) -> list[str]: + """Get a list of xunits attached to this xfstests run.""" + return sorted(self.__xunits)