summaryrefslogtreecommitdiffstats
path: root/tests/test_subcmds_wipe.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_subcmds_wipe.py')
-rw-r--r--tests/test_subcmds_wipe.py263
1 files changed, 263 insertions, 0 deletions
diff --git a/tests/test_subcmds_wipe.py b/tests/test_subcmds_wipe.py
new file mode 100644
index 00000000..ae515e3d
--- /dev/null
+++ b/tests/test_subcmds_wipe.py
@@ -0,0 +1,263 @@
1# Copyright (C) 2025 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
15import os
16import shutil
17from unittest import mock
18
19import pytest
20
21import project
22from subcmds import wipe
23
24
25def _create_mock_project(tempdir, name, objdir_path=None, has_changes=False):
26 """Creates a mock project with necessary attributes and directories."""
27 worktree = os.path.join(tempdir, name)
28 gitdir = os.path.join(tempdir, ".repo/projects", f"{name}.git")
29 if objdir_path:
30 objdir = objdir_path
31 else:
32 objdir = os.path.join(tempdir, ".repo/project-objects", f"{name}.git")
33
34 os.makedirs(worktree, exist_ok=True)
35 os.makedirs(gitdir, exist_ok=True)
36 os.makedirs(objdir, exist_ok=True)
37
38 proj = project.Project(
39 manifest=mock.MagicMock(),
40 name=name,
41 remote=mock.MagicMock(),
42 gitdir=gitdir,
43 objdir=objdir,
44 worktree=worktree,
45 relpath=name,
46 revisionExpr="main",
47 revisionId="abcd",
48 )
49
50 proj.HasChanges = mock.MagicMock(return_value=has_changes)
51
52 def side_effect_delete_worktree(force=False, verbose=False):
53 if os.path.exists(proj.worktree):
54 shutil.rmtree(proj.worktree)
55 if os.path.exists(proj.gitdir):
56 shutil.rmtree(proj.gitdir)
57 return True
58
59 proj.DeleteWorktree = mock.MagicMock(
60 side_effect=side_effect_delete_worktree
61 )
62
63 return proj
64
65
66def _run_wipe(all_projects, projects_to_wipe_names, options=None):
67 """Helper to run the Wipe command with mocked projects."""
68 cmd = wipe.Wipe()
69 cmd.manifest = mock.MagicMock()
70
71 def get_projects_mock(projects, all_manifests=False, **kwargs):
72 if projects is None:
73 return all_projects
74 names_to_find = set(projects)
75 return [p for p in all_projects if p.name in names_to_find]
76
77 cmd.GetProjects = mock.MagicMock(side_effect=get_projects_mock)
78
79 if options is None:
80 options = []
81
82 opts = cmd.OptionParser.parse_args(options + projects_to_wipe_names)[0]
83 cmd.CommonValidateOptions(opts, projects_to_wipe_names)
84 cmd.ValidateOptions(opts, projects_to_wipe_names)
85 cmd.Execute(opts, projects_to_wipe_names)
86
87
88def test_wipe_single_unshared_project(tmp_path):
89 """Test wiping a single project that is not shared."""
90 p1 = _create_mock_project(str(tmp_path), "project/one")
91 _run_wipe([p1], ["project/one"])
92
93 assert not os.path.exists(p1.worktree)
94 assert not os.path.exists(p1.gitdir)
95 assert not os.path.exists(p1.objdir)
96
97
98def test_wipe_multiple_unshared_projects(tmp_path):
99 """Test wiping multiple projects that are not shared."""
100 p1 = _create_mock_project(str(tmp_path), "project/one")
101 p2 = _create_mock_project(str(tmp_path), "project/two")
102 _run_wipe([p1, p2], ["project/one", "project/two"])
103
104 assert not os.path.exists(p1.worktree)
105 assert not os.path.exists(p1.gitdir)
106 assert not os.path.exists(p1.objdir)
107 assert not os.path.exists(p2.worktree)
108 assert not os.path.exists(p2.gitdir)
109 assert not os.path.exists(p2.objdir)
110
111
112def test_wipe_shared_project_no_force_raises_error(tmp_path):
113 """Test that wiping a shared project without --force raises an error."""
114 shared_objdir = os.path.join(
115 str(tmp_path), ".repo/project-objects", "shared.git"
116 )
117 p1 = _create_mock_project(
118 str(tmp_path), "project/one", objdir_path=shared_objdir
119 )
120 p2 = _create_mock_project(
121 str(tmp_path), "project/two", objdir_path=shared_objdir
122 )
123
124 with pytest.raises(wipe.Error) as e:
125 _run_wipe([p1, p2], ["project/one"])
126
127 assert "shared object directories" in str(e.value)
128 assert "project/one" in str(e.value)
129 assert "project/two" in str(e.value)
130
131 assert os.path.exists(p1.worktree)
132 assert os.path.exists(p1.gitdir)
133 assert os.path.exists(p2.worktree)
134 assert os.path.exists(p2.gitdir)
135 assert os.path.exists(shared_objdir)
136
137
138def test_wipe_shared_project_with_force(tmp_path):
139 """Test wiping a shared project with --force."""
140 shared_objdir = os.path.join(
141 str(tmp_path), ".repo/project-objects", "shared.git"
142 )
143 p1 = _create_mock_project(
144 str(tmp_path), "project/one", objdir_path=shared_objdir
145 )
146 p2 = _create_mock_project(
147 str(tmp_path), "project/two", objdir_path=shared_objdir
148 )
149
150 _run_wipe([p1, p2], ["project/one"], options=["--force"])
151
152 assert not os.path.exists(p1.worktree)
153 assert not os.path.exists(p1.gitdir)
154 assert os.path.exists(shared_objdir)
155 assert os.path.exists(p2.worktree)
156 assert os.path.exists(p2.gitdir)
157
158
159def test_wipe_all_sharing_projects(tmp_path):
160 """Test wiping all projects that share an object directory."""
161 shared_objdir = os.path.join(
162 str(tmp_path), ".repo/project-objects", "shared.git"
163 )
164 p1 = _create_mock_project(
165 str(tmp_path), "project/one", objdir_path=shared_objdir
166 )
167 p2 = _create_mock_project(
168 str(tmp_path), "project/two", objdir_path=shared_objdir
169 )
170
171 _run_wipe([p1, p2], ["project/one", "project/two"])
172
173 assert not os.path.exists(p1.worktree)
174 assert not os.path.exists(p1.gitdir)
175 assert not os.path.exists(p2.worktree)
176 assert not os.path.exists(p2.gitdir)
177 assert not os.path.exists(shared_objdir)
178
179
180def test_wipe_with_uncommitted_changes_raises_error(tmp_path):
181 """Test wiping a project with uncommitted changes raises an error."""
182 p1 = _create_mock_project(str(tmp_path), "project/one", has_changes=True)
183
184 with pytest.raises(wipe.Error) as e:
185 _run_wipe([p1], ["project/one"])
186
187 assert "uncommitted changes" in str(e.value)
188 assert "project/one" in str(e.value)
189
190 assert os.path.exists(p1.worktree)
191 assert os.path.exists(p1.gitdir)
192 assert os.path.exists(p1.objdir)
193
194
195def test_wipe_with_uncommitted_changes_with_force(tmp_path):
196 """Test wiping a project with uncommitted changes with --force."""
197 p1 = _create_mock_project(str(tmp_path), "project/one", has_changes=True)
198 _run_wipe([p1], ["project/one"], options=["--force"])
199
200 assert not os.path.exists(p1.worktree)
201 assert not os.path.exists(p1.gitdir)
202 assert not os.path.exists(p1.objdir)
203
204
205def test_wipe_uncommitted_and_shared_raises_combined_error(tmp_path):
206 """Test that uncommitted and shared projects raise a combined error."""
207 shared_objdir = os.path.join(
208 str(tmp_path), ".repo/project-objects", "shared.git"
209 )
210 p1 = _create_mock_project(
211 str(tmp_path),
212 "project/one",
213 objdir_path=shared_objdir,
214 has_changes=True,
215 )
216 p2 = _create_mock_project(
217 str(tmp_path), "project/two", objdir_path=shared_objdir
218 )
219
220 with pytest.raises(wipe.Error) as e:
221 _run_wipe([p1, p2], ["project/one"])
222
223 assert "uncommitted changes" in str(e.value)
224 assert "shared object directories" in str(e.value)
225 assert "project/one" in str(e.value)
226 assert "project/two" in str(e.value)
227
228 assert os.path.exists(p1.worktree)
229 assert os.path.exists(p1.gitdir)
230 assert os.path.exists(p2.worktree)
231 assert os.path.exists(p2.gitdir)
232 assert os.path.exists(shared_objdir)
233
234
235def test_wipe_shared_project_with_force_shared(tmp_path):
236 """Test wiping a shared project with --force-shared."""
237 shared_objdir = os.path.join(
238 str(tmp_path), ".repo/project-objects", "shared.git"
239 )
240 p1 = _create_mock_project(
241 str(tmp_path), "project/one", objdir_path=shared_objdir
242 )
243 p2 = _create_mock_project(
244 str(tmp_path), "project/two", objdir_path=shared_objdir
245 )
246
247 _run_wipe([p1, p2], ["project/one"], options=["--force-shared"])
248
249 assert not os.path.exists(p1.worktree)
250 assert not os.path.exists(p1.gitdir)
251 assert os.path.exists(shared_objdir)
252 assert os.path.exists(p2.worktree)
253 assert os.path.exists(p2.gitdir)
254
255
256def test_wipe_with_uncommitted_changes_with_force_uncommitted(tmp_path):
257 """Test wiping uncommitted changes with --force-uncommitted."""
258 p1 = _create_mock_project(str(tmp_path), "project/one", has_changes=True)
259 _run_wipe([p1], ["project/one"], options=["--force-uncommitted"])
260
261 assert not os.path.exists(p1.worktree)
262 assert not os.path.exists(p1.gitdir)
263 assert not os.path.exists(p1.objdir)