From aee3402c6815f6620f23125f786cbd972ae16ae4 Mon Sep 17 00:00:00 2001
From: TerryHowe <terrylhowe@gmail.com>
Date: Sun, 19 Jul 2015 08:30:14 -0600
Subject: [PATCH] Convert file names to regular expressions

Add the capability to the --no-discover option to convert file
names into regular sexpressions.  Also add the --path option that
converts a file or directory to a regular expression.  Create a
mutually exclusive argparse group for --regex, --path, and
--no-discover.

This change will allow the user to specify a file name instead of a
regular expression to match a particular set of tests e.g:

    tox -e py27 -- --path os_testr/tests/test_os_testr.py

Will run os_testr.tests.test_os_testr but you can use tab complete
to generate the name.

Change-Id: Ibfca2bc023aed44b1b87a0444559ab2a00303a70
---
 os_testr/os_testr.py            | 35 ++++++++++++++++++++++++---------
 os_testr/tests/test_os_testr.py | 10 +++++++---
 2 files changed, 33 insertions(+), 12 deletions(-)

diff --git a/os_testr/os_testr.py b/os_testr/os_testr.py
index 353868c..00d7cd4 100755
--- a/os_testr/os_testr.py
+++ b/os_testr/os_testr.py
@@ -29,11 +29,19 @@ def parse_args():
     parser.add_argument('--blacklist_file', '-b',
                         help='Path to a blacklist file, this file contains a'
                              ' separate regex exclude on each newline')
-    parser.add_argument('--regex', '-r',
-                        help='A normal testr selection regex. If a blacklist '
-                             'file is specified, the regex will be appended '
-                             'to the end of the generated regex from that '
-                             'file')
+    group = parser.add_mutually_exclusive_group()
+    group.add_argument('--regex', '-r',
+                       help='A normal testr selection regex. If a blacklist '
+                            'file is specified, the regex will be appended '
+                            'to the end of the generated regex from that '
+                            'file.')
+    group.add_argument('--path', metavar='FILE_OR_DIRECTORY',
+                       help='A file name or directory of tests to run.')
+    group.add_argument('--no-discover', '-n', metavar='TEST_ID',
+                       help="Takes in a single test to bypasses test "
+                            "discover and just excute the test specified. "
+                            "A file name may be used in place of a test "
+                            "name.")
     parser.add_argument('--pretty', '-p', dest='pretty', action='store_true',
                         help='Print pretty output from subunit-trace. This is '
                              'mutually exclusive with --subunit')
@@ -44,9 +52,6 @@ def parse_args():
                              'this is mutuall exclusive with --pretty')
     parser.add_argument('--list', '-l', action='store_true',
                         help='List all the tests which will be run.')
-    parser.add_argument('--no-discover', '-n', metavar='TEST_ID',
-                        help="Takes in a single test to bypasses test "
-                             "discover and just excute the test specified")
     parser.add_argument('--slowest', dest='slowest', action='store_true',
                         help="after the test run print the slowest tests")
     parser.add_argument('--no-slowest', dest='slowest', action='store_false',
@@ -112,6 +117,11 @@ def print_skips(regex, message):
         print('\n')
 
 
+def path_to_regex(path):
+    root, _ = os.path.splitext(path)
+    return root.replace('/', '.')
+
+
 def construct_regex(blacklist_file, regex, print_exclude):
     if not blacklist_file:
         exclude_regex = ''
@@ -248,7 +258,11 @@ def main():
         msg = "You can not use until_failure mode with pdb or no-discover"
         print(msg)
         exit(5)
-    exclude_regex = construct_regex(opts.blacklist_file, opts.regex,
+    if opts.path:
+        regex = path_to_regex(opts.path)
+    else:
+        regex = opts.regex
+    exclude_regex = construct_regex(opts.blacklist_file, regex,
                                     opts.print_exclude)
     if not os.path.isdir('.testrepository'):
         subprocess.call(['testr', 'init'])
@@ -259,6 +273,9 @@ def main():
     elif opts.pdb:
         exit(call_testtools_run(opts.pdb))
     else:
+        test_to_run = opts.no_discover
+        if test_to_run.find('/') != -1:
+            test_to_run = path_to_regex(test_to_run)
         exit(call_subunit_run(opts.no_discover, opts.pretty, opts.subunit))
 
 if __name__ == '__main__':
diff --git a/os_testr/tests/test_os_testr.py b/os_testr/tests/test_os_testr.py
index 3f64916..7b4ce22 100644
--- a/os_testr/tests/test_os_testr.py
+++ b/os_testr/tests/test_os_testr.py
@@ -19,10 +19,14 @@ test_os_testr
 Tests for `os_testr` module.
 """
 
+from os_testr import os_testr
 from os_testr.tests import base
 
 
-class TestOs_testr(base.TestCase):
+class Test_Construct_Regex(base.TestCase):
 
-    def test_something(self):
-        pass
+    def test_file_name(self):
+        result = os_testr.path_to_regex("tests/network/v2/test_net.py")
+        self.assertEqual("tests.network.v2.test_net", result)
+        result = os_testr.path_to_regex("openstack/tests/network/v2")
+        self.assertEqual("openstack.tests.network.v2", result)