Diana Picus | 3601bea | 2017-05-29 11:26:18 +0200 | [diff] [blame] | 1 | """Common TestCase used for testing llvm.py subcommands.""" |
| 2 | |
| 3 | import shutil |
| 4 | import os |
| 5 | import subprocess |
| 6 | import unittest |
| 7 | |
| 8 | from tempfile import mkdtemp |
| 9 | from uuid import uuid4 |
| 10 | |
| 11 | from linaropy.cd import cd |
| 12 | |
| 13 | |
| 14 | # TODO: move this somewhere more public (maybe linaropy?) |
| 15 | def debug(test): |
| 16 | """ |
| 17 | Decorator that dumps the output of any subprocess.CalledProcessError |
| 18 | exception. Use this to decorate a test function when you can't tell what the |
| 19 | problem is. |
| 20 | """ |
| 21 | def wrapper(*args, **kwargs): |
| 22 | # Catch any exceptions so we can dump all the output |
| 23 | try: |
Diana Picus | 5fdc892 | 2018-01-25 13:47:50 +0100 | [diff] [blame] | 24 | self = args[0] |
| 25 | self.maxDiff = None # So we can see large diffs between |
| 26 | # expected/actual |
Diana Picus | 3601bea | 2017-05-29 11:26:18 +0200 | [diff] [blame] | 27 | test(*args, **kwargs) |
| 28 | except subprocess.CalledProcessError as exc: |
| 29 | print("Error in {}:".format(test.__name__)) |
| 30 | print("Command {} exited with error code {}:\n{}".format( |
| 31 | exc.cmd, exc.returncode, exc.output)) |
| 32 | return wrapper |
| 33 | |
| 34 | |
Diana Picus | 9276b78 | 2018-01-24 14:40:46 +0100 | [diff] [blame] | 35 | def require_command_arg(requiredArg): |
| 36 | """ |
| 37 | Decorator that simplifies writing CLI tests that check that 'requiredArg' is |
| 38 | in fact required (i.e. that the command that we're testing will blow up if |
| 39 | we don't pass it the required argument). |
| 40 | |
| 41 | Use this to decorate test functions, e.g: |
| 42 | |
| 43 | @require_command_arg("--my-required-arg") |
| 44 | def test_my_required_arg_is_compulsory(self): |
| 45 | run_command_without_my_required_arg() |
| 46 | """ |
| 47 | def decorate(test): |
| 48 | def wrapper(*args, **kwargs): |
| 49 | self = args[0] |
| 50 | with self.assertRaises(subprocess.CalledProcessError) as context: |
| 51 | test(*args, **kwargs) |
| 52 | |
| 53 | self.assertRegex(str(context.exception.output), |
| 54 | "(.*\n)*the following arguments are required: {}(.*\n)*".format(requiredArg)) |
| 55 | return wrapper |
| 56 | return decorate |
| 57 | |
| 58 | |
Diana Picus | 3601bea | 2017-05-29 11:26:18 +0200 | [diff] [blame] | 59 | class LLVMTestCase(unittest.TestCase): |
| 60 | python = "python3" |
| 61 | script = os.path.join("scripts", "llvm.py") |
| 62 | |
| 63 | @classmethod |
Diana Picus | efc7bda | 2017-06-09 19:14:08 +0200 | [diff] [blame] | 64 | def create_dummy_commit(cls, commitMessage="Dummy commit"): |
| 65 | filename = "filethatshouldntexist" + str(uuid4()) |
Diana Picus | 3601bea | 2017-05-29 11:26:18 +0200 | [diff] [blame] | 66 | cls.run_quietly(["touch", filename]) |
Diana Picus | efc7bda | 2017-06-09 19:14:08 +0200 | [diff] [blame] | 67 | try: |
| 68 | cls.run_quietly(["git", "add", filename]) |
| 69 | cls.run_quietly(["git", "commit", "-m", commitMessage]) |
| 70 | except subprocess.CalledProcessError as exc: |
| 71 | print("Command {} exited with error code {}:\n{}".format( |
| 72 | exc.cmd, exc.returncode, exc.output)) |
Diana Picus | 3601bea | 2017-05-29 11:26:18 +0200 | [diff] [blame] | 73 | |
| 74 | @classmethod |
Diana Picus | efc7bda | 2017-06-09 19:14:08 +0200 | [diff] [blame] | 75 | def create_dummy_repo(cls, repopath, originpath=None): |
| 76 | if originpath is not None: |
| 77 | cls.run_quietly(["git", "clone", originpath, repopath]) |
| 78 | else: |
| 79 | if not os.path.isdir(repopath): |
| 80 | os.makedirs(repopath) |
| 81 | |
| 82 | with cd(repopath): |
| 83 | cls.run_quietly(["git", "init"]) |
Diana Picus | 3601bea | 2017-05-29 11:26:18 +0200 | [diff] [blame] | 84 | |
| 85 | with cd(repopath): |
Diana Picus | 3601bea | 2017-05-29 11:26:18 +0200 | [diff] [blame] | 86 | cls.create_dummy_commit() |
| 87 | |
| 88 | @classmethod |
| 89 | def add_worktree(cls, repopath, worktreepath, branch): |
| 90 | with cd(repopath): |
| 91 | cls.run_quietly(["git", "worktree", "add", worktreepath, |
| 92 | "-b", branch]) |
| 93 | |
Diana Picus | 3601bea | 2017-05-29 11:26:18 +0200 | [diff] [blame] | 94 | @staticmethod |
| 95 | def run_with_output(*args, **kwargs): |
| 96 | """Helper for running a command and capturing stdout and stderr""" |
| 97 | kwargs["stderr"] = subprocess.STDOUT |
| 98 | return str(subprocess.check_output(*args, **kwargs), 'utf-8') |
| 99 | |
| 100 | @staticmethod |
| 101 | def run_quietly(*args, **kwargs): |
| 102 | """ |
| 103 | Helper for running a command and ignoring stdout and stderr. Exceptions |
| 104 | are still thrown if something goes wrong |
| 105 | """ |
| 106 | kwargs["stdout"] = subprocess.DEVNULL |
| 107 | kwargs["stderr"] = subprocess.DEVNULL |
| 108 | return subprocess.check_call(*args, **kwargs) |
| 109 | |
| 110 | @classmethod |
| 111 | def command_with_defaults(cls, subcommand, *args, **kwargs): |
Diana Picus | 9f75686 | 2017-12-20 10:35:08 +0100 | [diff] [blame] | 112 | """Build a list representing an llvm subcommand with the given args.""" |
Diana Picus | 3601bea | 2017-05-29 11:26:18 +0200 | [diff] [blame] | 113 | command = [cls.python, cls.script] |
| 114 | |
Diana Picus | 3601bea | 2017-05-29 11:26:18 +0200 | [diff] [blame] | 115 | command.append(subcommand) |
| 116 | |
| 117 | if len(args): |
| 118 | command.extend(args) |
| 119 | |
| 120 | return command |