aboutsummaryrefslogtreecommitdiff
path: root/tests/cli/llvmtestcase.py
blob: f895c495cd93f56c3f28d8099eee57cc515e9b5c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
"""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