blob: 30f7ff2592c52d72dffe8138cbb8267b283003dd [file] [log] [blame]
"""Command line interface tests for llvmprojs.py
Note that although this uses the unittest framework, it does *not* contain unit
tests.
"""
import shutil
import os
import subprocess
import unittest
from tempfile import mkdtemp
from uuid import uuid4
from linaropy.cd import cd
from llvmtestcase import LLVMTestCase
class Testllvmprojs(LLVMTestCase):
@classmethod
def llvm_projects(cls, *args, **kwargs):
return cls.command_with_defaults("projects", *args, **kwargs)
@classmethod
def get_subproj_repo(cls, subproj):
return os.path.join(cls.repos, subproj)
@classmethod
def setUpClass(cls):
"""Create the file structure and environment that llvmprojs expects"""
cls.llvm_root = mkdtemp()
cls.repos = os.path.join(cls.llvm_root, "repos")
cls.all_repos = ("llvm", "clang", "compiler-rt", "lld", "lldb",
"libcxx", "libcxxabi", "libunwind", "test-suite")
# Create dummy repos
for reponame in cls.all_repos:
cls.create_dummy_repo(cls.get_subproj_repo(reponame))
@classmethod
def tearDownClass(cls):
shutil.rmtree(cls.llvm_root)
@classmethod
def setUp(cls):
cls.env = os.path.join(cls.llvm_root, "env" + str(uuid4()))
cls.llvm_src = os.path.join(cls.env, "llvm")
# Create LLVM worktree
cls.branch = "br" + str(uuid4())
cls.add_worktree(cls.get_subproj_repo("llvm"), cls.llvm_src,
cls.branch)
@classmethod
def tearDown(cls):
# Clean up the directories where we might have added subprojects.
# This isn't 100% clean, because we don't clean up the repos between
# tests (so any branches will remain), but it's good enough for the
# current tests.
for subprojdir in (os.path.join(cls.llvm_src, "projects"),
os.path.join(cls.llvm_src, "tools")):
if os.path.isdir(subprojdir):
shutil.rmtree(subprojdir)
os.makedirs(subprojdir)
# Run prune on the original repos, to remove any dangling worktrees.
for reponame in cls.all_repos:
repopath = cls.get_subproj_repo(reponame)
with cd(repopath):
cls.run_quietly(["git", "worktree", "prune"])
def test_repos_arg_is_compulsory(self):
"""
Test that we must pass in the repos for various combinations of input
args.
"""
with self.assertRaises(subprocess.CalledProcessError) as context:
self.run_with_output(self.llvm_projects(repos=None))
self.assertRegex(
str(context.exception.output),
"(.*\n)*.*the following arguments are required:(.*)--repos(.*\n)*")
with self.assertRaises(subprocess.CalledProcessError) as context:
self.run_with_output(
self.llvm_projects(
"--add", "clang", repos=None))
self.assertRegex(
str(context.exception.output),
"(.*\n)*.*the following arguments are required:(.*)--repos(.*\n)*")
with self.assertRaises(subprocess.CalledProcessError) as context:
self.run_with_output(
self.llvm_projects(
"--remove",
"clang",
repos=None))
self.assertRegex(
str(context.exception.output),
"(.*\n)*.*the following arguments are required:(.*)--repos(.*\n)*")
def test_env_arg_is_compulsory(self):
"""
Test that we must pass in the environment for various combinations of
input args.
"""
with self.assertRaises(subprocess.CalledProcessError) as context:
self.run_with_output(self.llvm_projects(env=None))
self.assertRegex(
str(context.exception.output),
"(.*\n)*.*the following arguments are required:(.*)--env(.*\n)*")
with self.assertRaises(subprocess.CalledProcessError) as context:
self.run_with_output(
self.llvm_projects(
"--add", "clang", env=None))
self.assertRegex(
str(context.exception.output),
"(.*\n)*.*the following arguments are required:(.*)--env(.*\n)*")
with self.assertRaises(subprocess.CalledProcessError) as context:
self.run_with_output(
self.llvm_projects(
"--remove", "clang", env=None))
self.assertRegex(
str(context.exception.output),
"(.*\n)*.*the following arguments are required:(.*)--env(.*\n)*")
def test_dump_empty_config(self):
"""
Test that we're correctly dumping an empty configuration (i.e. no
projects linked) when running llvmprojs without arguments.
"""
output = self.run_with_output(self.llvm_projects())
self.assertRegex(output, "Projects linked:.*\n.*none.*")
def test_add_remove_subprojects(self):
"""
Test that we can add and remove one or several subprojects.
"""
output = self.run_with_output(self.llvm_projects("--add", "clang"))
self.assertRegex(output, "Projects linked:.*\n.*clang.*")
output = self.run_with_output(
self.llvm_projects(
"--add", "libcxx", "lldb"))
self.assertRegex(
output,
"Projects linked:.*\n" +
".*clang.*\n" +
".*libcxx.*\n" +
".*lldb.*")
output = self.run_with_output(self.llvm_projects("--remove", "libcxx"))
self.assertRegex(output,
"Projects linked:.*\n" +
".*clang.*\n" +
".*lldb.*")
output = self.run_with_output(
self.llvm_projects(
"--remove", "clang", "lldb"))
self.assertRegex(output,
"Projects linked:.*\n" +
".*none.*")
def test_add_remove_invalid_subprojects(self):
"""
Test that we error out nicely when trying to add/remove invalid
subprojects.
"""
with self.assertRaises(subprocess.CalledProcessError) as context:
self.run_with_output(
self.llvm_projects(
"--add", "inventedsubproject"))
self.assertRegex(
str(context.exception.output),
"(.*\n)*.*invalid choice:.*inventedsubproject(.*\n)*")
with self.assertRaises(subprocess.CalledProcessError) as context:
self.run_with_output(
self.llvm_projects(
"--remove",
"inventedsubproject"))
self.assertRegex(
str(context.exception.output),
"(.*\n)*.*invalid choice:.*inventedsubproject(.*\n)*")
def test_duplicate_add_remove(self):
"""
Test that we don't crash when trying to add / remove the same subproject
twice with the same command.
"""
output = self.run_with_output(
self.llvm_projects(
"--add", "clang", "lld", "clang"))
self.assertRegex(output,
"Projects linked:.*\n" +
".*clang.*\n" +
".*lld.*")
output = self.run_with_output(
self.llvm_projects(
"--remove", "lld", "lld", "clang"))
self.assertRegex(output,
"Projects linked:.*\n" +
".*none.*")
def test_redundant_add_remove(self):
"""
Test that we can add a subproject that already exists (either because it
was added by our script or manually) or remove a subproject that doesn't
exist.
"""
self.add_worktree(self.get_subproj_repo("clang"),
os.path.join(self.llvm_src, "tools", "clang"),
self.branch)
self.add_worktree(
self.get_subproj_repo("compiler-rt"),
os.path.join(self.llvm_src, "projects", "compiler-rt"),
self.branch)
output = self.run_with_output(self.llvm_projects("--add", "clang"))
self.assertRegex(output,
"Projects linked:.*\n" +
".*clang.*")
output = self.run_with_output(
self.llvm_projects(
"--add", "compiler-rt", "lld"))
self.assertRegex(
output,
"Projects linked:.*\n" +
".*clang.*\n" +
".*compiler-rt.*\n" +
".*lld.*")
output = self.run_with_output(
self.llvm_projects(
"--remove", "lldb", "libcxx", "lld"))
self.assertRegex(
output,
"Projects linked:.*\n" +
".*clang.*\n" +
".*compiler-rt.*")
def test_simultaneous_add_remove(self):
"""
Test that we error out when someone is trying to add and remove the same
project with the same command.
"""
# Try the really basic case and make sure we're not touching anything
with self.assertRaises(subprocess.CalledProcessError) as context:
self.run_with_output(
self.llvm_projects(
"--add",
"clang",
"--remove",
"clang"))
self.assertRegex(
str(context.exception.output),
"(.*\n)*.*Can't add and remove clang at the same time(.*\n)*")
self.assertFalse(
os.path.exists(
os.path.join(self.llvm_src, "tools", "clang")))
# Try something a bit more complicated and make sure we're not touching
# anything
self.add_worktree(
self.get_subproj_repo("lld"),
os.path.join(self.llvm_src, "tools", "lld"),
self.branch)
with self.assertRaises(subprocess.CalledProcessError) as context:
self.run_with_output(
self.llvm_projects(
"--add",
"clang",
"lld",
"libcxx",
"--remove",
"lld",
"libcxx"))
self.assertRegex(
str(context.exception.output),
"(.*\n)*" +
".*Can't add and remove (lld|libcxx) at the same time(.*\n)*")
# Make sure we didn't touch anything
self.assertFalse(
os.path.exists(
os.path.join(self.llvm_src, "tools", "clang")))
self.assertTrue(
os.path.exists(
os.path.join(self.llvm_src, "tools", "lld")))
self.assertFalse(
os.path.exists(
os.path.join(self.llvm_src, "projects", "libcxx")))
def test_multiple_adds_removes(self):
"""
Test that we can have multiple --add and --remove options in the same
command and that only the last one of each kind matters.
"""
output = self.run_with_output(
self.llvm_projects(
"--add",
"libcxxabi",
"--remove",
"lld",
"lldb",
"--add",
"clang",
"libcxx",
"--remove",
"libunwind"))
self.assertRegex(output,
"Projects linked:.*\n" +
".*clang.*\n" +
".*libcxx.*\n")
output = self.run_with_output(
self.llvm_projects(
"--add",
"libunwind",
"libcxxabi",
"--remove",
"clang",
"libcxx",
"--add",
"compiler-rt",
"--remove",
"libcxxabi"))
self.assertRegex(output,
"Projects linked:.*\n" +
".*clang.*\n" +
".*compiler-rt.*\n" +
".*libcxx.*\n")
output = self.run_with_output(
self.llvm_projects(
"--add",
"libcxx",
"--remove",
"lld",
"--add",
"lld",
"--remove",
"libcxx"))
self.assertRegex(output,
"Projects linked:.*\n" +
".*clang.*\n" +
".*compiler-rt.*\n" +
".*lld.*\n")
def test_different_env(self):
"""
Test that we can have different environments in completely different
paths and they don't interfere when we try to add/remove projects.
"""
# Create a separate environment
new_env = mkdtemp()
new_branch = "br" + str(uuid4())
self.add_worktree(self.get_subproj_repo("llvm"),
os.path.join(new_env, "llvm"), new_branch)
# Check that we start with a clean slate in both the new environment and
# the one that's already set up
output = self.run_with_output(self.llvm_projects())
self.assertRegex(output, "Projects linked:.*\n.*none.*")
output = self.run_with_output(self.llvm_projects(env=new_env))
self.assertRegex(output, "Projects linked:.*\n.*none.*")
# Make sure that adding projects works
output = self.run_with_output(
self.llvm_projects(
"--add", "clang", "lld"))
self.assertRegex(output, "Projects linked:.*\n.*clang.*\n.*lld.*\n")
output = self.run_with_output(
self.llvm_projects(
"--add",
"libcxx",
"libcxxabi",
env=new_env))
self.assertRegex(output,
"Projects linked:.*\n.*libcxx.*\n.*libcxxabi.*\n.*")
output = self.run_with_output(self.llvm_projects())
self.assertRegex(output, "Projects linked:.*\n.*clang.*\n.*lld.*\n")
# Make sure that removing projects works
output = self.run_with_output(self.llvm_projects("--remove", "lld"))
self.assertRegex(output, "Projects linked:.*\n.*clang.*\n")
output = self.run_with_output(self.llvm_projects("--remove", "libcxx",
env=new_env))
self.assertRegex(output,
"Projects linked:.*\n.*libcxxabi.*\n.*")
output = self.run_with_output(self.llvm_projects())
self.assertRegex(output, "Projects linked:.*\n.*clang.*\n")
shutil.rmtree(new_env)