# Copyright 2023 (c) Anna Schumaker. """Tests the `xfstestsdb gc` command.""" import datetime import io import unittest import unittest.mock import xfstestsdb.xunit.gc import tests.xunit @unittest.mock.patch("xfstestsdb.sqlite.Connection.vacuum") @unittest.mock.patch("sys.stdout", new_callable=io.StringIO) class TestGC(unittest.TestCase): """Tests the `xfstestsdb xunit gc` command.""" def setUp(self): """Set up common variables.""" self.xfstestsdb = xfstestsdb.Command() self.xunit = self.xfstestsdb.commands["xunit"] self.gc = self.xunit.commands["gc"] def setup_runs(self, mock_stdout: io.StringIO): """Set up runs in the database and clear stdout.""" self.xfstestsdb.run(["new", "/dev/vda1"]) self.xfstestsdb.run(["new", "/dev/vda1"]) self.xfstestsdb.run(["new", "/dev/vda1"]) self.xfstestsdb.run(["xunit", "read", "1", str(tests.xunit.XUNIT_1)]) self.xfstestsdb.run(["xunit", "read", "3", str(tests.xunit.XUNIT_1)]) self.xfstestsdb.sql("DELETE FROM testcases WHERE xunitid=2") mock_stdout.seek(0) mock_stdout.truncate(0) def test_init(self, mock_stdout: io.StringIO, mock_vacuum: unittest.mock.Mock): """Check that the gc command was set up properly.""" self.assertIsInstance(self.gc, xfstestsdb.commands.Command) self.assertIsInstance(self.gc, xfstestsdb.xunit.gc.Command) self.assertEqual(self.xunit.subparser.choices["gc"], self.gc.parser) def test_gc_empty(self, mock_stdout: io.StringIO, mock_vacuum: unittest.mock.Mock): """Test garbage collecting an empty database.""" cur = self.xfstestsdb.sql("SELECT runid FROM xfstests_gc_runs") self.assertListEqual([row['runid'] for row in cur.fetchall()], []) self.xfstestsdb.run(["gc"]) self.assertEqual(mock_stdout.getvalue(), "") mock_vacuum.assert_not_called() def test_gc_no_testcases(self, mock_stdout: io.StringIO, mock_vacuum: unittest.mock.Mock): """Test garbage collecting runs with no testcases.""" self.setup_runs(mock_stdout) cur = self.xfstestsdb.sql("SELECT runid FROM xfstests_gc_runs") self.assertListEqual([row['runid'] for row in cur.fetchall()], [2, 3]) self.xfstestsdb.run(["gc"]) self.assertRegex(mock_stdout.getvalue(), "run #2 has been deleted\nrun #3 has been deleted") cur = self.xfstestsdb.sql("SELECT runid FROM xfstests_runs") self.assertListEqual([row['runid'] for row in cur.fetchall()], [1]) mock_vacuum.assert_called() def test_gc_expired(self, mock_stdout: io.StringIO, mock_vacuum: unittest.mock.Mock): """Test garbage collecting old runs.""" self.setup_runs(mock_stdout) self.xfstestsdb.run(["xunit", "read", "2", str(tests.xunit.XUNIT_1)]) utcnow = datetime.datetime.utcnow() expired = utcnow - datetime.timedelta(days=180, seconds=1) self.xfstestsdb.sql("""UPDATE xfstests_runs SET timestamp=? WHERE runid=1""", expired) not_expired = utcnow - datetime.timedelta(days=180, seconds=-1) self.xfstestsdb.sql("""UPDATE xfstests_runs SET timestamp=? WHERE runid=2""", not_expired) cur = self.xfstestsdb.sql("SELECT runid FROM xfstests_gc_runs") self.assertListEqual([row['runid'] for row in cur.fetchall()], [1, 3]) self.xfstestsdb.run(["gc"]) self.assertRegex(mock_stdout.getvalue(), "run #1 has been deleted\nrun #3 has been deleted") cur = self.xfstestsdb.sql("SELECT runid FROM xfstests_runs") self.assertListEqual([row['runid'] for row in cur.fetchall()], [2]) mock_vacuum.assert_called() def test_gc_expired_tagged(self, mock_stdout: io.StringIO, mock_vacuum: unittest.mock.Mock): """Test that we don't garbage collect expired runs that are tagged.""" self.setup_runs(mock_stdout) self.xfstestsdb.run(["xunit", "read", "2", str(tests.xunit.XUNIT_1)]) self.xfstestsdb.run(["tag", "1", "my-tag"]) self.xfstestsdb.run(["tag", "3", "my-tag"]) utcnow = datetime.datetime.utcnow() expired = utcnow - datetime.timedelta(days=181) self.xfstestsdb.sql("UPDATE xfstests_runs SET timestamp=?", expired) cur = self.xfstestsdb.sql("SELECT runid FROM xfstests_gc_runs") self.assertListEqual([row['runid'] for row in cur.fetchall()], [2, 3]) self.xfstestsdb.run(["gc"]) self.assertRegex(mock_stdout.getvalue(), "run #2 has been deleted\nrun #3 has been deleted") cur = self.xfstestsdb.sql("SELECT runid FROM xfstests_runs") self.assertListEqual([row['runid'] for row in cur.fetchall()], [1]) mock_vacuum.assert_called() def test_gc_dry_run(self, mock_stdout: io.StringIO, mock_vacuum: unittest.mock.Mock): """Test garbage collecting with the --dry-run option.""" self.setup_runs(mock_stdout) self.xfstestsdb.run(["gc", "--dry-run"]) self.assertRegex(mock_stdout.getvalue(), "run #2 would be deleted\nrun #3 would be deleted") cur = self.xfstestsdb.sql("SELECT runid FROM xfstests_runs") self.assertListEqual([row['runid'] for row in cur.fetchall()], [1, 2, 3]) mock_vacuum.assert_not_called()