Repository: DataBiosphere/toil

Issue:
parseSuiteTestOptions is not needed anymore
... since there are no tests processing command line options anymore. IOW, `--logLevel` and `--testLength` aren't used anymore.



Discussion:
Benedict, could you please confirm or  deny and reassign to me?

I think it is not used anymore, the only place I see it referenced is in setupClass in the **init**.py of the test module. So, I think you are good to delete.


Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -57,6 +57,8 @@ def finalize_options( self ):
 
     def run_tests( self ):
         import pytest
+        # Sanitize command line arguments to avoid confusing Toil code attempting to parse them
+        sys.argv[1:] = []
         errno = pytest.main( self.pytest_args )
         sys.exit( errno )
 
diff --git a/src/toil/lib/bioio.py b/src/toil/lib/bioio.py
--- a/src/toil/lib/bioio.py
+++ b/src/toil/lib/bioio.py
@@ -252,7 +252,7 @@ def getBasicOptionParser(usage="usage: %prog [options]", version="%prog 0.1", pa
 
     parser.add_option("--tempDirRoot", dest="tempDirRoot", type="string",
                       help="Path to where temporary directory containing all temp files are created, by default uses the current working directory as the base.",
-                      default=os.getcwd())
+                      default=tempfile.gettempdir())
 
     return parser
 
@@ -264,39 +264,8 @@ def parseBasicOptions(parser):
     setLoggingFromOptions(options)
 
     #Set up the temp dir root
-    if options.tempDirRoot == "None":
-        options.tempDirRoot = os.getcwd()
-
-    return options, args
-
-def parseSuiteTestOptions(parser=None):
-    if parser is None:
-        parser = getBasicOptionParser()
-
-    parser.add_option("--testLength", dest="testLength", type="string",
-                     help="Control the length of the tests either SHORT/MEDIUM/LONG/VERY_LONG. default=%default",
-                     default="SHORT")
-
-    parser.add_option("--saveError", dest="saveError", type="string",
-                     help="Directory in which to store the inputs of failed tests")
-
-    options, args = parseBasicOptions(parser)
-    logger.info("Parsed arguments")
-
-    if options.testLength == "SHORT":
-        TestStatus.setTestStatus(TestStatus.TEST_SHORT)
-    elif options.testLength == "MEDIUM":
-        TestStatus.setTestStatus(TestStatus.TEST_MEDIUM)
-    elif options.testLength == "LONG":
-        TestStatus.setTestStatus(TestStatus.TEST_LONG)
-    elif options.testLength == "VERY_LONG":
-        TestStatus.setTestStatus(TestStatus.TEST_VERY_LONG)
-    else:
-        parser.error('Unrecognised option for --testLength, %s. Options are SHORT, MEDIUM, LONG, VERY_LONG.' %
-                     options.testLength)
-
-    if options.saveError is not None:
-        TestStatus.setSaveErrorLocation(options.saveError)
+    if options.tempDirRoot == "None": # FIXME: Really, a string containing the word None?
+        options.tempDirRoot = tempfile.gettempdir()
 
     return options, args
 


Test patch:
diff --git a/src/toil/test/__init__.py b/src/toil/test/__init__.py
--- a/src/toil/test/__init__.py
+++ b/src/toil/test/__init__.py
@@ -11,56 +11,103 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+
 from __future__ import absolute_import
 import logging
 import os
-import shlex
 import tempfile
 import unittest
-import sys
-import uuid
+import shutil
+import re
+
+from bd2k.util.files import mkdir_p
 
 from toil.common import toilPackageDirPath
-from toil.lib.bioio import getBasicOptionParser, parseSuiteTestOptions
 
 log = logging.getLogger(__name__)
 
 
 class ToilTest(unittest.TestCase):
     """
-    A common base class for our tests. Please have every test case directly or indirectly inherit
-    this one.
+    A common base class for Toil tests. Please have every test case directly or indirectly
+    inherit this one.
+
+    When running tests you may optionally set the TOIL_TEST_TEMP environment variable to the path
+    of a directory where you want temporary test files be placed. The directory will be created
+    if it doesn't exist. The path may be relative in which case it will be assumed to be relative
+    to the project root. If TOIL_TEST_TEMP is not defined, temporary files and directories will
+    be created in the system's default location for such files and any temporary files or
+    directories left over from tests will be removed automatically removed during tear down.
+    Otherwise, left-over files will not be removed.
     """
 
-    orig_sys_argv = None
-
-    def getScriptPath(self, script_name):
-        return os.path.join(toilPackageDirPath(), 'utils', script_name + '.py')
+    _tempBaseDir = None
 
     @classmethod
     def setUpClass(cls):
         super(ToilTest, cls).setUpClass()
-        cls.orig_sys_argv = sys.argv[1:]
-        sys.argv[1:] = shlex.split(os.environ.get('TOIL_TEST_ARGS', ""))
-        parser = getBasicOptionParser()
-        options, args = parseSuiteTestOptions(parser)
-        sys.argv[1:] = args
+        cls._tempDirs = []
+        tempBaseDir = os.environ.get('TOIL_TEST_TEMP', None)
+        if tempBaseDir is not None and not os.path.isabs(tempBaseDir):
+            tempBaseDir = os.path.abspath(os.path.join(cls._projectRootPath(), tempBaseDir))
+            mkdir_p(tempBaseDir)
+        cls._tempBaseDir = tempBaseDir
+
+    @classmethod
+    def _getUtilScriptPath(cls, script_name):
+        return os.path.join(toilPackageDirPath(), 'utils', script_name + '.py')
+
+    @classmethod
+    def _projectRootPath(cls):
+        """
+        Returns the path to the project root, i.e. the directory that typically contains the .git
+        and src subdirectories. This method has limited utility. It only works if in "develop"
+        mode, since it assumes the existence of a src subdirectory which, in a regular install
+        wouldn't exist. Then again, in that mode project root has no meaning anyways.
+        """
+        assert re.search(r'__init__\.pyc?$', __file__)
+        projectRootPath = os.path.dirname(os.path.abspath(__file__))
+        packageComponents = __name__.split('.')
+        expectedSuffix = os.path.join('src', *packageComponents)
+        assert projectRootPath.endswith(expectedSuffix)
+        projectRootPath = projectRootPath[:-len(expectedSuffix)]
+        return projectRootPath
 
     @classmethod
     def tearDownClass(cls):
-        sys.argv[1:] = cls.orig_sys_argv
+        if cls._tempBaseDir is None:
+            while cls._tempDirs:
+                tempDir = cls._tempDirs.pop()
+                if os.path.exists(tempDir):
+                    shutil.rmtree(tempDir)
+        else:
+            cls._tempDirs = []
         super(ToilTest, cls).tearDownClass()
 
     def setUp(self):
-        log.info("Setting up %s", self.id())
+        log.info("Setting up %s ...", self.id())
         super(ToilTest, self).setUp()
 
+    def _createTempDir(self, purpose=None):
+        prefix = ['toil', 'test', self.id()]
+        if purpose: prefix.append(purpose)
+        prefix.append('')
+        temp_dir_path = tempfile.mkdtemp(dir=self._tempBaseDir, prefix='-'.join(prefix))
+        self._tempDirs.append(temp_dir_path)
+        return temp_dir_path
+
     def tearDown(self):
         super(ToilTest, self).tearDown()
-        log.info("Tearing down down %s", self.id())
+        log.info("Tore down %s", self.id())
 
     def _getTestJobStorePath(self):
-        return os.path.join(tempfile.gettempdir(), str(uuid.uuid4()))
+        path = self._createTempDir(purpose='jobstore')
+        # We only need a unique path, directory shouldn't actually exist. This of course is racy
+        # and insecure because another thread could now allocate the same path as a temporary
+        # directory. However, the built-in tempfile module randomizes the name temp dir suffixes
+        # reasonably well (1 in 63 ^ 6 chance of collision), making this an unlikely scenario.
+        os.rmdir(path)
+        return path
 
 
 def needs_aws(test_item):
diff --git a/src/toil/test/dependencies/dependenciesTest.py b/src/toil/test/dependencies/dependenciesTest.py
--- a/src/toil/test/dependencies/dependenciesTest.py
+++ b/src/toil/test/dependencies/dependenciesTest.py
@@ -47,20 +47,11 @@ class DependenciesTest(ToilTest):
 
     def setUp(self):
         super(DependenciesTest, self).setUp()
-        # A directory for the jobtree to be created in
-        self.toilDir = os.path.join(os.getcwd(), 'testToil')
-        self.tempDir = tempfile.mkdtemp(prefix='tempDir', dir=os.getcwd())
-
-
-    def tearDown(self):
-        super(DependenciesTest, self).tearDown()
-        if os.path.exists(self.toilDir):
-            shutil.rmtree(self.toilDir)
-        if os.path.exists(self.tempDir):
-            shutil.rmtree(self.tempDir)
-
+        self.jobStore = self._createTempDir('jobStore')
+        self.tempDir = self._createTempDir('tempDir')
 
     # FIXME: test methods can't have parameters, AFAIK (Hannes)
+
     @unittest.expectedFailure
     def testDependencies(self, batchSystem="singleMachine"):
         """
@@ -73,8 +64,8 @@ def fn(tree, maxCores, maxThreads, size, coresPerJob, sleepTime):
             """
             startTime = datetime.datetime.now()
 
-            if os.path.exists(self.toilDir):
-                shutil.rmtree(self.toilDir)
+            if os.path.exists(self.jobStore):
+                shutil.rmtree(self.jobStore)
 
             logFd, logName = tempfile.mkstemp(prefix="log", dir=self.tempDir)
 
@@ -89,7 +80,7 @@ def fn(tree, maxCores, maxThreads, size, coresPerJob, sleepTime):
                 tree = combTree(size)
 
             options = Job.Runner.getDefaultOptions()
-            options.jobStore = self.toilDir
+            options.jobStore = self.jobStore
             options.maxThreads = maxThreads
             options.batchSystem = batchSystem
             options.logFile = logName
diff --git a/src/toil/test/mesos/helloWorld.py b/src/toil/test/mesos/helloWorld.py
--- a/src/toil/test/mesos/helloWorld.py
+++ b/src/toil/test/mesos/helloWorld.py
@@ -43,7 +43,6 @@ def hello_world_child(job, hw):
     # If we want to SAVE our changes to this tmp file, we must write it out.
     with open(path, 'r') as r:
         with open('bar_bam.txt', 'w') as handle:
-            x = os.getcwd()
             for line in r.readlines():
                 handle.write(line)
 
diff --git a/src/toil/test/sort/sortTest.py b/src/toil/test/sort/sortTest.py
--- a/src/toil/test/sort/sortTest.py
+++ b/src/toil/test/sort/sortTest.py
@@ -52,21 +52,15 @@ def tearDown(self):
         if os.path.exists(self.tempDir):
             shutil.rmtree(self.tempDir)
 
-    def toilSortTest(self, testNo=1, batchSystem="singleMachine", jobStore='file',
-                     lines=10000, maxLineLength=10, N=10000):
+    def toilSortTest(self, jobStore, batchSystem, lines, N, testNo=1, maxLineLength=10):
         """
         Tests toil by sorting a file in parallel.
         """
         for test in xrange(testNo):
-            options = Job.Runner.getDefaultOptions()
-
-            # toil
-            if jobStore == 'file':
-                options.jobStore = self.jobStore
-            else:
-                options.jobStore = jobStore
 
             # Specify options
+            options = Job.Runner.getDefaultOptions()
+            options.jobStore = jobStore
             options.logLevel = getLogLevelString()
             options.retryCount = 2
             options.batchSystem = batchSystem
@@ -125,7 +119,7 @@ def toilSortTest(self, testNo=1, batchSystem="singleMachine", jobStore='file',
                 self.fail()
             except JobException:
                 pass
-                #self.assertTrue(e.message.endswith('left in toil workflow (workflow has finished successfully?)'))
+                # self.assertTrue(e.message.endswith('left in toil workflow (workflow has finished successfully?)'))
 
             # Now check the file is properly sorted..
             with open(tempSortFile, 'r') as fileHandle:
@@ -137,18 +131,27 @@ def testToilSortOnAWS(self):
         """Tests scriptTree/toil by sorting a file in parallel.
         """
         self.toilSortTest(jobStore="aws:us-west-2:sort-test-%s" % uuid4(),
+                          batchSystem='singleMachine',
                           lines=100, N=100)
 
     @needs_aws
     @needs_mesos
-    def testScriptTree_SortSimpleOnAWSWithMesos(self):
+    def testToilSortOnAWSWithMesos(self):
+        self._startMesos()
+        try:
+            self.toilSortTest(jobStore="aws:us-west-2:sort-test-%s" % uuid4(),
+                              batchSystem="mesos",
+                              lines=100, N=100)
+        finally:
+            self._stopMesos()
+
+    @needs_mesos
+    def testToilSortWithMesos(self):
         self._startMesos()
         try:
-            self.toilSortTest(testNo=1,
+            self.toilSortTest(jobStore=self._getTestJobStorePath(),
                               batchSystem="mesos",
-                              jobStore="aws:us-west-2:sort-test-%s" % uuid4(),
-                              lines=100,
-                              N=100)
+                              lines=100, N=100)
         finally:
             self._stopMesos()
 
@@ -156,7 +159,9 @@ def testToilSort(self):
         """
         Tests scriptTree/toil by sorting a file in parallel.
         """
-        self.toilSortTest()
+        self.toilSortTest(jobStore=self._getTestJobStorePath(),
+                          batchSystem='singleMachine',
+                          lines=10000, N=10000)
 
     # The following functions test the functions in the test!
 
@@ -230,19 +235,23 @@ def checkEqual(i, j):
         print "false", j
     assert i == j
 
+
 def loadFile(file):
     with open(file, 'r') as fileHandle:
         return fileHandle.readlines()
 
+
 def getRandomLine(maxLineLength):
     return "".join(
         [random.choice(['a', 'c', 't', 'g', "A", "C", "G", "T", "N", "X", "Y", "Z"]) for i in
          xrange(maxLineLength)]) + "\n"
 
+
 def makeFileToSort(fileName, lines=10, maxLineLength=10):
     with open(fileName, 'w') as fileHandle:
         for line in xrange(lines):
             fileHandle.write(getRandomLine(maxLineLength))
 
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/src/toil/test/src/jobEncapsulationTest.py b/src/toil/test/src/jobEncapsulationTest.py
--- a/src/toil/test/src/jobEncapsulationTest.py
+++ b/src/toil/test/src/jobEncapsulationTest.py
@@ -31,7 +31,7 @@ def testEncapsulation(self):
         class.
         """
         # Temporary file
-        outFile = getTempFile(rootDir=os.getcwd())
+        outFile = getTempFile(rootDir=self._createTempDir())
         try:
             # Make a job graph
             a = T.wrapFn(f, "A", outFile)
diff --git a/src/toil/test/src/jobServiceTest.py b/src/toil/test/src/jobServiceTest.py
--- a/src/toil/test/src/jobServiceTest.py
+++ b/src/toil/test/src/jobServiceTest.py
@@ -28,7 +28,7 @@ def testService(self):
         Tests the creation of a Job.Service.
         """
         # Temporary file
-        outFile = getTempFile(rootDir=os.getcwd())
+        outFile = getTempFile(rootDir=self._createTempDir())
         try:
             # Wire up the services/jobs
             t = Job.wrapFn(f, "1", outFile)
diff --git a/src/toil/test/src/jobTest.py b/src/toil/test/src/jobTest.py
--- a/src/toil/test/src/jobTest.py
+++ b/src/toil/test/src/jobTest.py
@@ -38,7 +38,7 @@ def testStatic(self):
           
         Follow on is marked by ->
         """
-        outFile = getTempFile(rootDir=os.getcwd())
+        outFile = getTempFile(rootDir=self._createTempDir())
         try:
 
             # Create the jobs
diff --git a/src/toil/test/utils/utilsTest.py b/src/toil/test/utils/utilsTest.py
--- a/src/toil/test/utils/utilsTest.py
+++ b/src/toil/test/utils/utilsTest.py
@@ -17,12 +17,12 @@
 from subprocess import CalledProcessError
 
 from toil.lib.bioio import system
-from toil.lib.bioio import getTempDirectory
 from toil.lib.bioio import getTempFile
 from toil.test.sort.sortTest import makeFileToSort, checkEqual
 from toil.common import toilPackageDirPath
 from toil.test import ToilTest
 
+
 class UtilsTest(ToilTest):
     """
     Tests the scriptTree toil-script compiler.
@@ -30,10 +30,10 @@ class UtilsTest(ToilTest):
 
     def setUp(self):
         super(UtilsTest, self).setUp()
-        self.tempDir = getTempDirectory(os.getcwd())
+        self.tempDir = self._createTempDir()
         self.tempFile = getTempFile(rootDir=self.tempDir)
         self.outputFile = getTempFile(rootDir=self.tempDir)
-        self.toilDir = os.path.join(self.tempDir, "testToil")
+        self.toilDir = os.path.join(self.tempDir, "jobstore")
         self.assertFalse(os.path.exists(self.toilDir))
         self.lines = 1000
         self.maxLineLength = 10
@@ -43,19 +43,26 @@ def setUp(self):
         with open(self.tempFile, 'r') as fileHandle:
             self.correctSort = fileHandle.readlines()
             self.correctSort.sort()
-            
+
     def tearDown(self):
         ToilTest.tearDown(self)
         system("rm -rf %s" % self.tempDir)
 
+    @property
+    def toilMain(self):
+        return self._getUtilScriptPath('toilMain')
+
+    @property
+    def sort(self):
+        return os.path.join(toilPackageDirPath(), "test", "sort", "sort.py")
+
     def testUtilsSort(self):
         """
         Tests the restart, status and stats commands of the toil command line utility using the
         sort example.
         """
         # Get the sort command to run
-        rootPath = os.path.join(toilPackageDirPath(), "test", "sort")
-        toilCommandString = ("{rootPath}/sort.py "
+        toilCommandString = ("{self.sort} "
                              "--jobStore {self.toilDir} "
                              "--logLevel=DEBUG "
                              "--fileToSort={self.tempFile} "
@@ -66,17 +73,17 @@ def testUtilsSort(self):
         self.assertRaises(CalledProcessError, system, toilCommandString + " --restart")
         # Check that trying to run it in restart mode does not create the jobStore
         self.assertFalse(os.path.exists(self.toilDir))
-        
+
         # Status command
-        rootPath = os.path.join(toilPackageDirPath(), "utils")
-        toilStatusCommandString = ("{rootPath}/toilMain.py status "
-                                   "--jobStore {self.toilDir} --failIfNotComplete".format(**locals()))
+        toilStatusCommandString = ("{self.toilMain} status "
+                                   "--jobStore {self.toilDir} "
+                                   "--failIfNotComplete".format(**locals()))
 
         # Run the script for the first time
         try:
             system(toilCommandString)
             finished = True
-        except CalledProcessError: #This happens when the script fails due to having unfinished jobs
+        except CalledProcessError:  # This happens when the script fails due to having unfinished jobs
             self.assertRaises(CalledProcessError, system, toilStatusCommandString)
             finished = False
         self.assertTrue(os.path.exists(self.toilDir))
@@ -89,46 +96,45 @@ def testUtilsSort(self):
             try:
                 system(toilCommandString + " --restart")
                 finished = True
-            except CalledProcessError: #This happens when the script fails due to having unfinished jobs
+            except CalledProcessError:  # This happens when the script fails due to having unfinished jobs
                 self.assertRaises(CalledProcessError, system, toilStatusCommandString)
-                
-        #Check the toil status command does not issue an exception
+
+        # Check the toil status command does not issue an exception
         system(toilStatusCommandString)
 
         # Check if we try to launch after its finished that we get a JobException
         self.assertRaises(CalledProcessError, system, toilCommandString + " --restart")
 
         # Check we can run 'toil stats'
-        rootPath = os.path.join(toilPackageDirPath(), "utils")
-        toilStatsString = ("{rootPath}/toilMain.py stats "
-                           "--jobStore {self.toilDir} --pretty".format(**locals()))
+        toilStatsString = ("{self.toilMain} stats "
+                           "--jobStore {self.toilDir} "
+                           "--pretty".format(**locals()))
         system(toilStatsString)
 
         # Check the file is properly sorted
         with open(self.tempFile, 'r') as fileHandle:
             l2 = fileHandle.readlines()
             checkEqual(self.correctSort, l2)
-            
+
     def testUtilsStatsSort(self):
         """
         Tests the stats commands on a complete run of the stats test.
         """
         # Get the sort command to run
-        rootPath = os.path.join(toilPackageDirPath(), "test", "sort")
-        toilCommandString = ("{rootPath}/sort.py "
+        toilCommandString = ("{self.sort} "
                              "--jobStore {self.toilDir} "
                              "--logLevel=DEBUG "
                              "--fileToSort={self.tempFile} "
                              "--N {self.N} --stats "
                              "--retryCount 99".format(**locals()))
-    
+
         # Run the script for the first time
         system(toilCommandString)
         self.assertTrue(os.path.exists(self.toilDir))
 
         # Check we can run 'toil stats'
         rootPath = os.path.join(toilPackageDirPath(), "utils")
-        toilStatsString = ("{rootPath}/toilMain.py stats "
+        toilStatsString = ("{self.toilMain} stats "
                            "--jobStore {self.toilDir} --pretty".format(**locals()))
         system(toilStatsString)
 
@@ -137,5 +143,6 @@ def testUtilsStatsSort(self):
             l2 = fileHandle.readlines()
             checkEqual(self.correctSort, l2)
 
+
 if __name__ == '__main__':
     unittest.main()