"""Common TestCase used for testing llvm.py subcommands.""" import shutil import os import subprocess import unittest from tempfile import mkdtemp from uuid import uuid4 from linaropy.cd import cd # TODO: move this somewhere more public (maybe linaropy?) def debug(test): """ Decorator that dumps the output of any subprocess.CalledProcessError exception. Use this to decorate a test function when you can't tell what the problem is. """ def wrapper(*args, **kwargs): # Catch any exceptions so we can dump all the output try: self = args[0] self.maxDiff = None # So we can see large diffs between # expected/actual test(*args, **kwargs) except subprocess.CalledProcessError as exc: print("Error in {}:".format(test.__name__)) print("Command {} exited with error code {}:\n{}".format( exc.cmd, exc.returncode, exc.output)) return wrapper def require_command_arg(requiredArg): """ Decorator that simplifies writing CLI tests that check that 'requiredArg' is in fact required (i.e. that the command that we're testing will blow up if we don't pass it the required argument). Use this to decorate test functions, e.g: @require_command_arg("--my-required-arg") def test_my_required_arg_is_compulsory(self): run_command_without_my_required_arg() """ def decorate(test): def wrapper(*args, **kwargs): self = args[0] with self.assertRaises(subprocess.CalledProcessError) as context: test(*args, **kwargs) self.assertRegex(str(context.exception.output), "(.*\n)*the following arguments are required: {}(.*\n)*".format(requiredArg)) return wrapper return decorate class LLVMTestCase(unittest.TestCase): python = "python3" script = os.path.join("scripts", "llvm.py") @classmethod def create_dummy_commit(cls, commitMessage="Dummy commit"): filename = "filethatshouldntexist" + str(uuid4()) cls.run_quietly(["touch", filename]) try: cls.run_quietly(["git", "add", filename]) cls.run_quietly(["git", "commit", "-m", commitMessage]) except subprocess.CalledProcessError as exc: print("Command {} exited with error code {}:\n{}".format( exc.cmd, exc.returncode, exc.output)) @classmethod def create_dummy_repo(cls, repopath, originpath=None): if originpath is not None: cls.run_quietly(["git", "clone", originpath, repopath]) else: if not os.path.isdir(repopath): os.makedirs(repopath) with cd(repopath): cls.run_quietly(["git", "init"]) with cd(repopath): cls.create_dummy_commit() @classmethod def add_worktree(cls, repopath, worktreepath, branch): with cd(repopath): cls.run_quietly(["git", "worktree", "add", worktreepath, "-b", branch]) @staticmethod def run_with_output(*args, **kwargs): """Helper for running a command and capturing stdout and stderr""" kwargs["stderr"] = subprocess.STDOUT return str(subprocess.check_output(*args, **kwargs), 'utf-8') @staticmethod def run_quietly(*args, **kwargs): """ Helper for running a command and ignoring stdout and stderr. Exceptions are still thrown if something goes wrong """ kwargs["stdout"] = subprocess.DEVNULL kwargs["stderr"] = subprocess.DEVNULL return subprocess.check_call(*args, **kwargs) @classmethod def command_with_defaults(cls, subcommand, *args, **kwargs): """Build a list representing an llvm subcommand with the given args.""" command = [cls.python, cls.script] command.append(subcommand) if len(args): command.extend(args) return command