blob: 30f7ff2592c52d72dffe8138cbb8267b283003dd [file] [log] [blame]
Diana Picus3b2ef822016-10-13 16:53:18 +03001"""Command line interface tests for llvmprojs.py
2
3Note that although this uses the unittest framework, it does *not* contain unit
4tests.
5
6"""
7
8import shutil
9import os
10import subprocess
11import unittest
12
Diana Picus3b2ef822016-10-13 16:53:18 +030013from tempfile import mkdtemp
14from uuid import uuid4
15
16from linaropy.cd import cd
Diana Picus3601bea2017-05-29 11:26:18 +020017from llvmtestcase import LLVMTestCase
Diana Picus3b2ef822016-10-13 16:53:18 +030018
19
Diana Picus3601bea2017-05-29 11:26:18 +020020class Testllvmprojs(LLVMTestCase):
Diana Picusb4307602017-04-05 19:48:39 +020021
Diana Picus3b2ef822016-10-13 16:53:18 +030022 @classmethod
Diana Picus81089db2017-05-05 22:26:49 +020023 def llvm_projects(cls, *args, **kwargs):
Diana Picus3601bea2017-05-29 11:26:18 +020024 return cls.command_with_defaults("projects", *args, **kwargs)
Diana Picus81089db2017-05-05 22:26:49 +020025
26 @classmethod
Diana Picusefc7bda2017-06-09 19:14:08 +020027 def get_subproj_repo(cls, subproj):
28 return os.path.join(cls.repos, subproj)
29
30 @classmethod
Diana Picus3b2ef822016-10-13 16:53:18 +030031 def setUpClass(cls):
32 """Create the file structure and environment that llvmprojs expects"""
33 cls.llvm_root = mkdtemp()
34 cls.repos = os.path.join(cls.llvm_root, "repos")
35
36 cls.all_repos = ("llvm", "clang", "compiler-rt", "lld", "lldb",
37 "libcxx", "libcxxabi", "libunwind", "test-suite")
38
Diana Picus3b2ef822016-10-13 16:53:18 +030039 # Create dummy repos
40 for reponame in cls.all_repos:
Diana Picus3601bea2017-05-29 11:26:18 +020041 cls.create_dummy_repo(cls.get_subproj_repo(reponame))
Diana Picus3b2ef822016-10-13 16:53:18 +030042
43 @classmethod
44 def tearDownClass(cls):
45 shutil.rmtree(cls.llvm_root)
46
47 @classmethod
48 def setUp(cls):
Diana Picus81089db2017-05-05 22:26:49 +020049 cls.env = os.path.join(cls.llvm_root, "env" + str(uuid4()))
50 cls.llvm_src = os.path.join(cls.env, "llvm")
Diana Picus3b2ef822016-10-13 16:53:18 +030051
52 # Create LLVM worktree
53 cls.branch = "br" + str(uuid4())
Diana Picus3601bea2017-05-29 11:26:18 +020054 cls.add_worktree(cls.get_subproj_repo("llvm"), cls.llvm_src,
55 cls.branch)
Diana Picus3b2ef822016-10-13 16:53:18 +030056
Diana Picus3b2ef822016-10-13 16:53:18 +030057 @classmethod
58 def tearDown(cls):
59 # Clean up the directories where we might have added subprojects.
60 # This isn't 100% clean, because we don't clean up the repos between
61 # tests (so any branches will remain), but it's good enough for the
62 # current tests.
63 for subprojdir in (os.path.join(cls.llvm_src, "projects"),
64 os.path.join(cls.llvm_src, "tools")):
65 if os.path.isdir(subprojdir):
66 shutil.rmtree(subprojdir)
67 os.makedirs(subprojdir)
68
69 # Run prune on the original repos, to remove any dangling worktrees.
70 for reponame in cls.all_repos:
Diana Picus3601bea2017-05-29 11:26:18 +020071 repopath = cls.get_subproj_repo(reponame)
Diana Picus3b2ef822016-10-13 16:53:18 +030072 with cd(repopath):
73 cls.run_quietly(["git", "worktree", "prune"])
74
Diana Picus81089db2017-05-05 22:26:49 +020075 def test_repos_arg_is_compulsory(self):
76 """
77 Test that we must pass in the repos for various combinations of input
78 args.
79 """
80 with self.assertRaises(subprocess.CalledProcessError) as context:
81 self.run_with_output(self.llvm_projects(repos=None))
82
83 self.assertRegex(
84 str(context.exception.output),
85 "(.*\n)*.*the following arguments are required:(.*)--repos(.*\n)*")
86
87 with self.assertRaises(subprocess.CalledProcessError) as context:
88 self.run_with_output(
89 self.llvm_projects(
90 "--add", "clang", repos=None))
91
92 self.assertRegex(
93 str(context.exception.output),
94 "(.*\n)*.*the following arguments are required:(.*)--repos(.*\n)*")
95
96 with self.assertRaises(subprocess.CalledProcessError) as context:
97 self.run_with_output(
98 self.llvm_projects(
99 "--remove",
100 "clang",
101 repos=None))
102
103 self.assertRegex(
104 str(context.exception.output),
105 "(.*\n)*.*the following arguments are required:(.*)--repos(.*\n)*")
106
107 def test_env_arg_is_compulsory(self):
108 """
109 Test that we must pass in the environment for various combinations of
110 input args.
111 """
112 with self.assertRaises(subprocess.CalledProcessError) as context:
113 self.run_with_output(self.llvm_projects(env=None))
114
115 self.assertRegex(
116 str(context.exception.output),
117 "(.*\n)*.*the following arguments are required:(.*)--env(.*\n)*")
118
119 with self.assertRaises(subprocess.CalledProcessError) as context:
120 self.run_with_output(
121 self.llvm_projects(
122 "--add", "clang", env=None))
123
124 self.assertRegex(
125 str(context.exception.output),
126 "(.*\n)*.*the following arguments are required:(.*)--env(.*\n)*")
127
128 with self.assertRaises(subprocess.CalledProcessError) as context:
129 self.run_with_output(
130 self.llvm_projects(
131 "--remove", "clang", env=None))
132
133 self.assertRegex(
134 str(context.exception.output),
135 "(.*\n)*.*the following arguments are required:(.*)--env(.*\n)*")
136
Diana Picus3b2ef822016-10-13 16:53:18 +0300137 def test_dump_empty_config(self):
138 """
139 Test that we're correctly dumping an empty configuration (i.e. no
140 projects linked) when running llvmprojs without arguments.
141 """
Diana Picus81089db2017-05-05 22:26:49 +0200142 output = self.run_with_output(self.llvm_projects())
Diana Picusb4307602017-04-05 19:48:39 +0200143 self.assertRegex(output, "Projects linked:.*\n.*none.*")
Diana Picus3b2ef822016-10-13 16:53:18 +0300144
145 def test_add_remove_subprojects(self):
146 """
147 Test that we can add and remove one or several subprojects.
148 """
Diana Picus81089db2017-05-05 22:26:49 +0200149 output = self.run_with_output(self.llvm_projects("--add", "clang"))
Diana Picusb4307602017-04-05 19:48:39 +0200150 self.assertRegex(output, "Projects linked:.*\n.*clang.*")
Diana Picus3b2ef822016-10-13 16:53:18 +0300151
Diana Picus81089db2017-05-05 22:26:49 +0200152 output = self.run_with_output(
153 self.llvm_projects(
154 "--add", "libcxx", "lldb"))
Diana Picusb4307602017-04-05 19:48:39 +0200155 self.assertRegex(
Diana Picus3b2ef822016-10-13 16:53:18 +0300156 output,
157 "Projects linked:.*\n" +
158 ".*clang.*\n" +
159 ".*libcxx.*\n" +
160 ".*lldb.*")
161
Diana Picus81089db2017-05-05 22:26:49 +0200162 output = self.run_with_output(self.llvm_projects("--remove", "libcxx"))
Diana Picusb4307602017-04-05 19:48:39 +0200163 self.assertRegex(output,
164 "Projects linked:.*\n" +
165 ".*clang.*\n" +
166 ".*lldb.*")
Diana Picus3b2ef822016-10-13 16:53:18 +0300167
Diana Picus81089db2017-05-05 22:26:49 +0200168 output = self.run_with_output(
169 self.llvm_projects(
170 "--remove", "clang", "lldb"))
Diana Picusb4307602017-04-05 19:48:39 +0200171 self.assertRegex(output,
172 "Projects linked:.*\n" +
173 ".*none.*")
Diana Picus3b2ef822016-10-13 16:53:18 +0300174
175 def test_add_remove_invalid_subprojects(self):
176 """
177 Test that we error out nicely when trying to add/remove invalid
178 subprojects.
179 """
180 with self.assertRaises(subprocess.CalledProcessError) as context:
Diana Picus81089db2017-05-05 22:26:49 +0200181 self.run_with_output(
182 self.llvm_projects(
183 "--add", "inventedsubproject"))
Diana Picus3b2ef822016-10-13 16:53:18 +0300184
Diana Picusb4307602017-04-05 19:48:39 +0200185 self.assertRegex(
186 str(context.exception.output),
Diana Picus3b2ef822016-10-13 16:53:18 +0300187 "(.*\n)*.*invalid choice:.*inventedsubproject(.*\n)*")
188
189 with self.assertRaises(subprocess.CalledProcessError) as context:
Diana Picus81089db2017-05-05 22:26:49 +0200190 self.run_with_output(
191 self.llvm_projects(
192 "--remove",
193 "inventedsubproject"))
Diana Picus3b2ef822016-10-13 16:53:18 +0300194
Diana Picusb4307602017-04-05 19:48:39 +0200195 self.assertRegex(
196 str(context.exception.output),
Diana Picus3b2ef822016-10-13 16:53:18 +0300197 "(.*\n)*.*invalid choice:.*inventedsubproject(.*\n)*")
198
199 def test_duplicate_add_remove(self):
200 """
201 Test that we don't crash when trying to add / remove the same subproject
202 twice with the same command.
203 """
Diana Picus81089db2017-05-05 22:26:49 +0200204 output = self.run_with_output(
205 self.llvm_projects(
206 "--add", "clang", "lld", "clang"))
Diana Picusb4307602017-04-05 19:48:39 +0200207 self.assertRegex(output,
208 "Projects linked:.*\n" +
209 ".*clang.*\n" +
210 ".*lld.*")
Diana Picus3b2ef822016-10-13 16:53:18 +0300211
Diana Picus81089db2017-05-05 22:26:49 +0200212 output = self.run_with_output(
213 self.llvm_projects(
214 "--remove", "lld", "lld", "clang"))
Diana Picusb4307602017-04-05 19:48:39 +0200215 self.assertRegex(output,
216 "Projects linked:.*\n" +
217 ".*none.*")
Diana Picus3b2ef822016-10-13 16:53:18 +0300218
219 def test_redundant_add_remove(self):
220 """
221 Test that we can add a subproject that already exists (either because it
222 was added by our script or manually) or remove a subproject that doesn't
223 exist.
224 """
Diana Picus3601bea2017-05-29 11:26:18 +0200225 self.add_worktree(self.get_subproj_repo("clang"),
226 os.path.join(self.llvm_src, "tools", "clang"),
227 self.branch)
228 self.add_worktree(
229 self.get_subproj_repo("compiler-rt"),
Diana Picus3b2ef822016-10-13 16:53:18 +0300230 os.path.join(self.llvm_src, "projects", "compiler-rt"),
231 self.branch)
232
Diana Picus81089db2017-05-05 22:26:49 +0200233 output = self.run_with_output(self.llvm_projects("--add", "clang"))
Diana Picusb4307602017-04-05 19:48:39 +0200234 self.assertRegex(output,
235 "Projects linked:.*\n" +
236 ".*clang.*")
Diana Picus3b2ef822016-10-13 16:53:18 +0300237
Diana Picus81089db2017-05-05 22:26:49 +0200238 output = self.run_with_output(
239 self.llvm_projects(
240 "--add", "compiler-rt", "lld"))
Diana Picusb4307602017-04-05 19:48:39 +0200241 self.assertRegex(
Diana Picus3b2ef822016-10-13 16:53:18 +0300242 output,
243 "Projects linked:.*\n" +
244 ".*clang.*\n" +
245 ".*compiler-rt.*\n" +
246 ".*lld.*")
247
Diana Picus81089db2017-05-05 22:26:49 +0200248 output = self.run_with_output(
249 self.llvm_projects(
250 "--remove", "lldb", "libcxx", "lld"))
Diana Picusb4307602017-04-05 19:48:39 +0200251 self.assertRegex(
Diana Picus3b2ef822016-10-13 16:53:18 +0300252 output,
253 "Projects linked:.*\n" +
254 ".*clang.*\n" +
255 ".*compiler-rt.*")
256
257 def test_simultaneous_add_remove(self):
258 """
259 Test that we error out when someone is trying to add and remove the same
260 project with the same command.
261 """
262 # Try the really basic case and make sure we're not touching anything
263 with self.assertRaises(subprocess.CalledProcessError) as context:
Diana Picus81089db2017-05-05 22:26:49 +0200264 self.run_with_output(
265 self.llvm_projects(
266 "--add",
267 "clang",
268 "--remove",
269 "clang"))
Diana Picus3b2ef822016-10-13 16:53:18 +0300270
Diana Picusb4307602017-04-05 19:48:39 +0200271 self.assertRegex(
272 str(context.exception.output),
Diana Picus3b2ef822016-10-13 16:53:18 +0300273 "(.*\n)*.*Can't add and remove clang at the same time(.*\n)*")
274
275 self.assertFalse(
276 os.path.exists(
277 os.path.join(self.llvm_src, "tools", "clang")))
278
279 # Try something a bit more complicated and make sure we're not touching
280 # anything
Diana Picus3601bea2017-05-29 11:26:18 +0200281 self.add_worktree(
282 self.get_subproj_repo("lld"),
Diana Picus3b2ef822016-10-13 16:53:18 +0300283 os.path.join(self.llvm_src, "tools", "lld"),
284 self.branch)
285
286 with self.assertRaises(subprocess.CalledProcessError) as context:
Diana Picus81089db2017-05-05 22:26:49 +0200287 self.run_with_output(
288 self.llvm_projects(
289 "--add",
290 "clang",
291 "lld",
292 "libcxx",
293 "--remove",
294 "lld",
295 "libcxx"))
Diana Picusb4307602017-04-05 19:48:39 +0200296 self.assertRegex(
297 str(context.exception.output),
Diana Picus3b2ef822016-10-13 16:53:18 +0300298 "(.*\n)*" +
299 ".*Can't add and remove (lld|libcxx) at the same time(.*\n)*")
300
301 # Make sure we didn't touch anything
302 self.assertFalse(
303 os.path.exists(
304 os.path.join(self.llvm_src, "tools", "clang")))
305 self.assertTrue(
306 os.path.exists(
307 os.path.join(self.llvm_src, "tools", "lld")))
308 self.assertFalse(
309 os.path.exists(
310 os.path.join(self.llvm_src, "projects", "libcxx")))
311
312 def test_multiple_adds_removes(self):
313 """
314 Test that we can have multiple --add and --remove options in the same
315 command and that only the last one of each kind matters.
316 """
Diana Picus81089db2017-05-05 22:26:49 +0200317 output = self.run_with_output(
318 self.llvm_projects(
319 "--add",
320 "libcxxabi",
321 "--remove",
322 "lld",
323 "lldb",
324 "--add",
325 "clang",
326 "libcxx",
327 "--remove",
328 "libunwind"))
Diana Picusb4307602017-04-05 19:48:39 +0200329 self.assertRegex(output,
330 "Projects linked:.*\n" +
331 ".*clang.*\n" +
332 ".*libcxx.*\n")
Diana Picus3b2ef822016-10-13 16:53:18 +0300333
Diana Picus81089db2017-05-05 22:26:49 +0200334 output = self.run_with_output(
335 self.llvm_projects(
336 "--add",
337 "libunwind",
338 "libcxxabi",
339 "--remove",
340 "clang",
341 "libcxx",
342 "--add",
343 "compiler-rt",
344 "--remove",
345 "libcxxabi"))
Diana Picusb4307602017-04-05 19:48:39 +0200346 self.assertRegex(output,
347 "Projects linked:.*\n" +
348 ".*clang.*\n" +
349 ".*compiler-rt.*\n" +
350 ".*libcxx.*\n")
Diana Picus3b2ef822016-10-13 16:53:18 +0300351
Diana Picus81089db2017-05-05 22:26:49 +0200352 output = self.run_with_output(
353 self.llvm_projects(
354 "--add",
355 "libcxx",
356 "--remove",
357 "lld",
358 "--add",
359 "lld",
360 "--remove",
361 "libcxx"))
Diana Picusb4307602017-04-05 19:48:39 +0200362 self.assertRegex(output,
363 "Projects linked:.*\n" +
364 ".*clang.*\n" +
365 ".*compiler-rt.*\n" +
366 ".*lld.*\n")
Diana Picus81089db2017-05-05 22:26:49 +0200367
368 def test_different_env(self):
369 """
370 Test that we can have different environments in completely different
371 paths and they don't interfere when we try to add/remove projects.
372 """
373 # Create a separate environment
374 new_env = mkdtemp()
375 new_branch = "br" + str(uuid4())
Diana Picus3601bea2017-05-29 11:26:18 +0200376 self.add_worktree(self.get_subproj_repo("llvm"),
377 os.path.join(new_env, "llvm"), new_branch)
Diana Picus81089db2017-05-05 22:26:49 +0200378
379 # Check that we start with a clean slate in both the new environment and
380 # the one that's already set up
381 output = self.run_with_output(self.llvm_projects())
382 self.assertRegex(output, "Projects linked:.*\n.*none.*")
383
384 output = self.run_with_output(self.llvm_projects(env=new_env))
385 self.assertRegex(output, "Projects linked:.*\n.*none.*")
386
387 # Make sure that adding projects works
388 output = self.run_with_output(
389 self.llvm_projects(
390 "--add", "clang", "lld"))
391 self.assertRegex(output, "Projects linked:.*\n.*clang.*\n.*lld.*\n")
392
393 output = self.run_with_output(
394 self.llvm_projects(
395 "--add",
396 "libcxx",
397 "libcxxabi",
398 env=new_env))
399 self.assertRegex(output,
400 "Projects linked:.*\n.*libcxx.*\n.*libcxxabi.*\n.*")
401
402 output = self.run_with_output(self.llvm_projects())
403 self.assertRegex(output, "Projects linked:.*\n.*clang.*\n.*lld.*\n")
404
405 # Make sure that removing projects works
406 output = self.run_with_output(self.llvm_projects("--remove", "lld"))
407 self.assertRegex(output, "Projects linked:.*\n.*clang.*\n")
408
409 output = self.run_with_output(self.llvm_projects("--remove", "libcxx",
410 env=new_env))
411 self.assertRegex(output,
412 "Projects linked:.*\n.*libcxxabi.*\n.*")
413
414 output = self.run_with_output(self.llvm_projects())
415 self.assertRegex(output, "Projects linked:.*\n.*clang.*\n")
416
417 shutil.rmtree(new_env)