Remove some duplication from the tests

Get rid of some of the duplication from the command line interface tests
by extracting a decorator that checks that the proper exception is
thrown when we run a command without one of its required arguments.

Change-Id: Ifd2b93b7ff1bb249c002f1b03c0a242feb1f39f9
diff --git a/tests/cli/llvmtestcase.py b/tests/cli/llvmtestcase.py
index 0f29ce6..f895c49 100644
--- a/tests/cli/llvmtestcase.py
+++ b/tests/cli/llvmtestcase.py
@@ -32,6 +32,30 @@
     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")
diff --git a/tests/cli/testllvmbuild.py b/tests/cli/testllvmbuild.py
index a395d0f..e7295b0 100644
--- a/tests/cli/testllvmbuild.py
+++ b/tests/cli/testllvmbuild.py
@@ -10,7 +10,7 @@
 from subprocess import CalledProcessError
 from tempfile import mkdtemp
 
-from llvmtestcase import LLVMTestCase, debug
+from llvmtestcase import LLVMTestCase, require_command_arg, debug
 
 
 def create_empty_file(path):
@@ -29,14 +29,10 @@
     def tearDown(self):
         rmtree(self.buildDir)
 
+    @require_command_arg("--build-dir")
     def test_build_dir_is_compulsory(self):
         """Test that we get an error if we don't pass the build dir."""
-        with self.assertRaises(CalledProcessError) as context:
-            self.run_with_output(self.llvm_build())
-
-        self.assertRegex(
-            str(context.exception.output),
-            "(.*\n)*the following arguments are required: --build-dir(.*\n)*")
+        self.run_with_output(self.llvm_build())
 
     def test_dry_run(self):
         """
diff --git a/tests/cli/testllvmconfigure.py b/tests/cli/testllvmconfigure.py
index cfd95d8..8304587 100644
--- a/tests/cli/testllvmconfigure.py
+++ b/tests/cli/testllvmconfigure.py
@@ -9,7 +9,8 @@
 from tempfile import mkdtemp
 from uuid import uuid4
 
-from llvmtestcase import LLVMTestCase, debug
+from llvmtestcase import LLVMTestCase, require_command_arg, debug
+
 
 class Testllvmconfigure(LLVMTestCase):
 
@@ -27,25 +28,16 @@
         cls.llvm_worktree = path.join(cls.env, "llvm")
         cls.add_worktree(cls.llvm_repo, cls.llvm_worktree, "br")
 
+    @require_command_arg("--source-dir")
     def test_source_dir_is_compulsory(self):
         """Test that we get an error if we don't pass the source dir."""
-        with self.assertRaises(CalledProcessError) as context:
-            self.run_with_output(
-                    self.llvm_configure("--build-dir", "anywhere"))
+        self.run_with_output(self.llvm_configure("--build-dir", "anywhere"))
 
-        self.assertRegex(
-            str(context.exception.output),
-            "(.*\n)*the following arguments are required: --source-dir(.*\n)*")
-
+    @require_command_arg("--build-dir")
     def test_build_dir_is_compulsory(self):
         """Test that we get an error if we don't pass the build dir."""
-        with self.assertRaises(CalledProcessError) as context:
-            self.run_with_output(
-                    self.llvm_configure("--source-dir", self.llvm_worktree))
-
-        self.assertRegex(
-            str(context.exception.output),
-            "(.*\n)*the following arguments are required: --build-dir(.*\n)*")
+        self.run_with_output(
+            self.llvm_configure("--source-dir", self.llvm_worktree))
 
     def test_default_args(self):
         """Test that we can get a simple configure command."""
diff --git a/tests/cli/testllvmprojects.py b/tests/cli/testllvmprojects.py
index 32cead4..d0f6d1d 100644
--- a/tests/cli/testllvmprojects.py
+++ b/tests/cli/testllvmprojects.py
@@ -14,7 +14,7 @@
 from uuid import uuid4
 
 from linaropy.cd import cd
-from llvmtestcase import LLVMTestCase
+from llvmtestcase import LLVMTestCase, require_command_arg
 
 
 class Testllvmprojs(LLVMTestCase):
@@ -98,36 +98,26 @@
                 "--remove",
                 "clang"))
 
+    @require_command_arg("--source-dir")
     def test_source_dir_is_compulsory(self):
         """
         Test that we must pass in the source dir for various combinations of
         input args.
         """
-        with self.assertRaises(subprocess.CalledProcessError) as context:
-            self.run_with_output(self.llvm_projects())
+        self.run_with_output(self.llvm_projects())
 
-        self.assertRegex(
-            str(context.exception.output),
-            "(.*\n)*.*the following arguments are required:(.*)--source-dir(.*\n)*")
+    @require_command_arg("--source-dir")
+    def test_source_dir_ir_compulsory_for_add(self):
+        self.run_with_output(
+            self.llvm_projects(
+                "--repos", self.repos,
+                "--add", "clang", env=None))
 
-        with self.assertRaises(subprocess.CalledProcessError) as context:
-            self.run_with_output(
-                self.llvm_projects(
-                    "--repos", self.repos,
-                    "--add", "clang", env=None))
-
-        self.assertRegex(
-            str(context.exception.output),
-            "(.*\n)*.*the following arguments are required:(.*)--source-dir(.*\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:(.*)--source-dir(.*\n)*")
+    @require_command_arg("--source-dir")
+    def test_source_dir_ir_compulsory_for_remove(self):
+        self.run_with_output(
+            self.llvm_projects(
+                "--remove", "clang", env=None))
 
     def test_dump_empty_config(self):
         """
diff --git a/tests/cli/testllvmpush.py b/tests/cli/testllvmpush.py
index a704185..0bcca66 100644
--- a/tests/cli/testllvmpush.py
+++ b/tests/cli/testllvmpush.py
@@ -15,7 +15,7 @@
 
 from linaropy.cd import cd
 from linaropy.git.clone import Clone
-from llvmtestcase import LLVMTestCase, debug
+from llvmtestcase import LLVMTestCase, require_command_arg, debug
 
 
 class Testllvmpush(LLVMTestCase):
@@ -87,14 +87,10 @@
             with cd(repopath):
                 cls.run_quietly(["git", "worktree", "prune"])
 
+    @require_command_arg("--source-dir")
     def test_source_dir_ir_compulsory(self):
         """Test that we get an error if we don't pass the source dir."""
-        with self.assertRaises(subprocess.CalledProcessError) as context:
-            output = self.run_with_output(self.llvm_push())
-
-        self.assertRegex(
-            str(context.exception.output),
-            "(.*\n)*the following arguments are required: --source-dir(.*\n)*")
+        self.run_with_output(self.llvm_push())
 
     def test_push(self):
         with cd(self.llvm_src):