summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--subcmds/gc.py11
-rw-r--r--tests/test_subcmds_gc.py82
2 files changed, 92 insertions, 1 deletions
diff --git a/subcmds/gc.py b/subcmds/gc.py
index 4e5bcabff..a23d5e068 100644
--- a/subcmds/gc.py
+++ b/subcmds/gc.py
@@ -284,7 +284,16 @@ class Gc(Command):
284 args, all_manifests=not opt.this_manifest_only 284 args, all_manifests=not opt.this_manifest_only
285 ) 285 )
286 286
287 ret = self.delete_unused_projects(projects, opt) 287 # If the user specified projects, fetch the global list separately
288 # to avoid deleting untargeted projects.
289 if args:
290 all_projects = self.GetProjects(
291 [], all_manifests=not opt.this_manifest_only
292 )
293 else:
294 all_projects = projects
295
296 ret = self.delete_unused_projects(all_projects, opt)
288 if ret != 0: 297 if ret != 0:
289 return ret 298 return ret
290 299
diff --git a/tests/test_subcmds_gc.py b/tests/test_subcmds_gc.py
new file mode 100644
index 000000000..708f4a7ec
--- /dev/null
+++ b/tests/test_subcmds_gc.py
@@ -0,0 +1,82 @@
1# Copyright (C) 2026 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Unittests for the subcmds/gc.py module."""
16
17import unittest
18from unittest import mock
19
20from subcmds import gc
21
22
23class GcCommand(unittest.TestCase):
24 """Tests for gc command."""
25
26 def setUp(self):
27 self.cmd = gc.Gc()
28 self.opt, self.args = self.cmd.OptionParser.parse_args([])
29 self.opt.this_manifest_only = False
30 self.opt.repack = False
31
32 self.mock_get_projects = mock.patch.object(
33 self.cmd, "GetProjects"
34 ).start()
35
36 self.mock_delete = mock.patch.object(
37 self.cmd, "delete_unused_projects", return_value=0
38 ).start()
39
40 self.mock_repack = mock.patch.object(
41 self.cmd, "repack_projects", return_value=0
42 ).start()
43
44 def tearDown(self):
45 mock.patch.stopall()
46
47 def test_gc_no_args(self):
48 """Test gc without specific projects."""
49 self.mock_get_projects.return_value = ["all_projects"]
50
51 self.cmd.Execute(self.opt, [])
52
53 self.mock_get_projects.assert_called_once_with([], all_manifests=True)
54 self.mock_delete.assert_called_once_with(["all_projects"], self.opt)
55 self.mock_repack.assert_not_called()
56
57 def test_gc_with_args(self):
58 """Test gc with specific projects uses all_projects for delete."""
59 self.mock_get_projects.side_effect = [["projA"], ["all_projects"]]
60 self.opt.repack = True
61
62 self.cmd.Execute(self.opt, ["projA"])
63
64 self.mock_get_projects.assert_has_calls(
65 [
66 mock.call(["projA"], all_manifests=True),
67 mock.call([], all_manifests=True),
68 ]
69 )
70
71 self.mock_delete.assert_called_once_with(["all_projects"], self.opt)
72 self.mock_repack.assert_called_once_with(["projA"], self.opt)
73
74 def test_gc_exit_on_delete_failure(self):
75 """Test gc exits if delete_unused_projects fails."""
76 self.mock_get_projects.return_value = ["all_projects"]
77 self.mock_delete.return_value = 1
78 self.opt.repack = True
79
80 ret = self.cmd.Execute(self.opt, [])
81 self.assertEqual(ret, 1)
82 self.mock_repack.assert_not_called()