Coverage for tests / test_cliCmdRemoveRuns.py: 14%
80 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 23:37 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 23:37 +0000
1# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This software is dual licensed under the GNU General Public License and also
10# under a 3-clause BSD license. Recipients may choose which of these licenses
11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12# respectively. If you choose the GPL option then the following text applies
13# (but note that there is still no warranty even if you opt for BSD instead):
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program. If not, see <http://www.gnu.org/licenses/>.
28"""Unit tests for daf_butler CLI remove-runs subcommand."""
30import os
31import unittest
33from lsst.daf.butler import DatasetType
34from lsst.daf.butler.cli import butler
35from lsst.daf.butler.cli.cmd._remove_runs import (
36 abortedMsg,
37 didRemoveDatasetsMsg,
38 didRemoveRunsMsg,
39 mustBeUnlinkedMsg,
40 noRunCollectionsMsg,
41 removedRunsMsg,
42 willRemoveDatasetsMsg,
43 willRemoveRunsMsg,
44 willUnlinkMsg,
45)
46from lsst.daf.butler.cli.utils import LogCliRunner, clickResultMsg
47from lsst.daf.butler.tests.utils import MetricTestRepo
49TESTDIR = os.path.abspath(os.path.dirname(__file__))
52class RemoveCollectionTest(unittest.TestCase):
53 """Test the butler remove-runs command."""
55 def setUp(self):
56 self.runner = LogCliRunner()
58 def test_removeRuns(self):
59 with self.runner.isolated_filesystem():
60 root = "repo"
61 repo = MetricTestRepo(root, configFile=os.path.join(TESTDIR, "config/basic/butler.yaml"))
62 self.enterContext(repo.butler)
63 # Add a dataset type that will have no datasets to make sure it
64 # isn't printed.
65 repo.butler.registry.registerDatasetType(
66 DatasetType("no_datasets", repo.butler.dimensions.empty, "StructuredDataDict")
67 )
69 # Execute remove-runs but say no, check for expected outputs.
70 result = self.runner.invoke(butler.cli, ["remove-runs", root, "ingest*"], input="no")
71 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
72 self.assertIn(willRemoveRunsMsg, result.output)
73 self.assertIn(abortedMsg, result.output)
74 self.assertNotIn("no_datasets", result.output)
75 self.assertIn(
76 "ingest/run",
77 list(repo.butler.registry.queryCollections()),
78 )
80 # Add the run to a CHAINED collection.
81 parentCollection = "aParentCollection"
82 result = self.runner.invoke(
83 butler.cli, f"collection-chain {root} {parentCollection} ingest/run".split()
84 )
85 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
86 result = self.runner.invoke(butler.cli, ["query-collections", root])
87 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
88 self.assertIn(parentCollection, result.output)
90 # Execute remove-runs but say no, check for expected outputs
91 # including the CHAINED collection.
92 result = self.runner.invoke(butler.cli, ["remove-runs", root, "ingest*"], input="no")
93 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
94 self.assertIn(willRemoveRunsMsg, result.output)
95 self.assertIn(willRemoveDatasetsMsg, result.output)
96 self.assertIn(
97 willUnlinkMsg.format(run="ingest/run", parents=f'"{parentCollection}"'), result.output
98 )
99 self.assertIn(abortedMsg, result.output)
100 self.assertNotIn("no_datasets", result.output)
101 result = self.runner.invoke(butler.cli, ["query-collections", root])
102 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
103 self.assertIn("ingest/run", result.output)
104 self.assertIn(parentCollection, result.output)
106 # Do the same remove-runs command, but say yes.
107 result = self.runner.invoke(butler.cli, ["remove-runs", root, "ingest*"], input="yes")
108 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
109 self.assertIn(willRemoveRunsMsg, result.output)
110 self.assertIn(willRemoveDatasetsMsg, result.output)
111 self.assertIn(removedRunsMsg, result.output)
112 result = self.runner.invoke(butler.cli, ["query-collections", root])
113 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
114 self.assertNotIn("ingest/run", result.output)
115 self.assertIn(parentCollection, result.output)
117 # Now they've been deleted, try again and check for "none found".
118 result = self.runner.invoke(butler.cli, ["remove-runs", root, "ingest*"])
119 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
120 self.assertIn(noRunCollectionsMsg, result.output)
122 # Remake the repo and check --no-confirm option.
123 root = "repo1"
124 repo1 = MetricTestRepo(root, configFile=os.path.join(TESTDIR, "config/basic/butler.yaml"))
125 repo1.butler.close()
127 # Add the run to a CHAINED collection.
128 parentCollection = "parent"
129 result = self.runner.invoke(
130 butler.cli, f"collection-chain {root} {parentCollection} ingest/run".split()
131 )
132 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
133 result = self.runner.invoke(butler.cli, ["query-collections", root])
134 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
135 self.assertIn("ingest/run", result.output)
136 self.assertIn(parentCollection, result.output)
138 # Execute remove-runs with --no-confirm, should fail because there
139 # is a parent CHAINED collection.
140 result = self.runner.invoke(butler.cli, ["remove-runs", root, "ingest*", "--no-confirm"])
141 self.assertNotEqual(result.exit_code, 0, clickResultMsg(result))
142 self.assertIn(
143 mustBeUnlinkedMsg.format(run="ingest/run", parents=f'"{parentCollection}"'), result.output
144 )
145 result = self.runner.invoke(butler.cli, ["query-collections", root])
146 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
147 self.assertIn("ingest/run", result.output)
148 self.assertIn(parentCollection, result.output)
150 # Execute remove-runs with --no-confirm and --force
151 result = self.runner.invoke(
152 butler.cli, ["remove-runs", root, "ingest*", "--no-confirm", "--force"]
153 )
154 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
155 self.assertIn(didRemoveRunsMsg, result.output)
156 self.assertIn(didRemoveDatasetsMsg, result.output)
157 result = self.runner.invoke(butler.cli, ["query-collections", root])
158 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
159 self.assertNotIn("ingest/run", result.output)
160 self.assertIn(parentCollection, result.output)
162 # Execute cmd looking for a non-existant collection
163 result = self.runner.invoke(butler.cli, ["remove-runs", root, "foo"])
164 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
165 self.assertIn(noRunCollectionsMsg, result.output)
168if __name__ == "__main__":
169 unittest.main()