Commits

Dave Abrahams  committed 05cdf82

Added auto_test.py, which runs the tests in an appropriate clean environment.

Test slaves will need git, hg, bzr, svn and the Python virtualenv
package installed (and in $PATH). All other prerequisites will be
installed by auto_test.py.

  • Participants
  • Parent commits c077869

Comments (0)

Files changed (2)

File tests/auto_test.py

+import sys, os
+from subprocess import check_call, PIPE
+from path import Path
+import shutil
+from tempfile import mkdtemp, gettempdir
+
+exe = '.EXE' if sys.platform == 'win32' else ''
+
+def create_virtualenv(where):
+    save_argv = sys.argv
+    
+    try:
+        import virtualenv
+        sys.argv = ['virtualenv', '--quiet', '--no-site-packages', where]
+        virtualenv.main()
+    finally: 
+        sys.argv = save_argv
+
+    return virtualenv.path_locations(where)
+
+def rmtree(path):
+    # From pathutils by Michael Foord: http://www.voidspace.org.uk/python/pathutils.html
+    def onerror(func, path, exc_info):
+        """
+        Error handler for ``shutil.rmtree``.
+
+        If the error is due to an access error (read only file)
+        it attempts to add write permission and then retries.
+
+        If the error is for another reason it re-raises the error.
+
+        Usage : ``shutil.rmtree(path, onerror=onerror)``
+
+        """
+        import stat
+        if not os.access(path, os.W_OK):
+            # Is the error an access error ?
+            os.chmod(path, stat.S_IWUSR)
+            func(path)
+        else:
+            raise
+
+    if Path(path).exists:
+        shutil.rmtree(path, onerror=onerror)
+
+def system(*args):
+    check_call(args, stdout=PIPE, shell=(sys.platform=='win32'))
+
+def call(*args):
+    check_call(args)
+
+def assert_in_path(exe):
+    system(exe, '--version')
+
+def main(argv):
+    here = Path(sys.path[0])
+    script_name = Path(__file__).name
+
+    if not (here/script_name).exists:
+        here = Path(__file__).abspath.folder
+        assert (here/script_name).exists, "Can't locate directory of this script"
+
+    # Make sure all external tools are set up to be used.
+    print >> sys.stderr, 'Checking for installed prerequisites in PATH:',
+    for tool in 'git', 'hg', 'bzr', 'svn':
+        print >> sys.stderr, tool,'...',
+        assert_in_path(tool)
+    print >> sys.stderr, 'ok'
+
+    pip_root = here.folder
+
+    #
+    # Delete everything that could lead to stale test results
+    #
+    print >> sys.stderr, 'Cleaning ...',
+    for dirpath, dirnames, filenames in os.walk(pip_root):
+        for f in filenames:
+            if f.endswith('.pyc'):
+                os.unlink(Path(dirpath)/f)
+    rmtree(pip_root/'build')
+    rmtree(pip_root/'dist')
+    print >> sys.stderr, 'ok'
+    
+    save_dir = os.getcwd()
+    temp_dir = mkdtemp('-pip_auto_test')
+    try:
+        os.chdir(temp_dir)
+
+        #
+        # Prepare a clean, writable workspace
+        #
+        print >> sys.stderr, 'Preparing test environment ...',
+        venv, lib, include, bin = create_virtualenv(temp_dir)
+
+        abs_bin = Path(bin).abspath
+
+        # Make sure it's first in PATH
+        os.environ['PATH'] = str(
+            Path.pathsep.join(( abs_bin, os.environ['PATH'] ))
+            )
+
+        #
+        # Install python module testing prerequisites
+        #
+        pip = abs_bin/'pip'+exe
+        download_cache = '--download-cache=' \
+            + Path(gettempdir())/'pip-test-download-cache'
+        call(pip, 'install', '-q', download_cache, 'virtualenv')
+        call(pip, 'install', '-q', download_cache, 'nose')
+        # for now, we need a pre-release version of scripttest
+        call(pip, 'install', '-q', download_cache, 'scripttest')
+        print >> sys.stderr, 'ok'
+        nosetests = abs_bin/'nosetests'+exe
+        call( nosetests, '-w', pip_root/'tests', *argv[1:] )
+
+    finally:
+        os.chdir(save_dir)
+        rmtree(temp_dir)
+
+
+if __name__ == '__main__':
+    main( sys.argv )

File tests/path.py

+# -*- coding: utf-8 -*-
+# Author: Aziz Köksal
+import os, shutil
+
+class Path(unicode):
+  """ Models a path in an object oriented way. """
+  sep = os.sep # File system path separator: '/' or '\'.
+  pathsep = os.pathsep # Separator in the PATH environment variable.
+
+  def __new__(cls, *paths):
+    return unicode.__new__(cls, os.path.join(*paths) if len(paths) else '')
+
+  def __div__(self, path):
+    """ Joins this path with another path. """
+    """ path_obj / 'bc.d' """
+    """ path_obj / path_obj2 """
+    return Path(self, path)
+
+  def __rdiv__(self, path):
+    """ Joins this path with another path. """
+    """ "/home/a" / path_obj """
+    return Path(path, self)
+
+  def __idiv__(self, path):
+    """ Like __div__ but also assigns to the variable. """
+    """ path_obj /= 'bc.d' """
+    return Path(self, path)
+
+  def __floordiv__(self, paths):
+    """ Returns a list of paths prefixed with 'self'. """
+    """ '/home/a' // [bc.d, ef.g] -> [/home/a/bc.d, /home/a/ef.g] """
+    return [Path(self, path) for path in paths]
+
+  def __add__(self, path):
+    """ Path('/home/a') + 'bc.d' -> '/home/abc.d' """
+    return Path(unicode(self) + path)
+
+  def __radd__(self, path):
+    """ '/home/a' + Path('bc.d') -> '/home/abc.d' """
+    return Path(path + unicode(self))
+
+  def __repr__(self):
+    return u"Path(%s)" % unicode.__repr__(self)
+
+  @property
+  def name(self):
+    """ '/home/a/bc.d' -> 'bc.d' """
+    return os.path.basename(self)
+
+  @property
+  def namebase(self):
+    """ '/home/a/bc.d' -> 'bc' """
+    return self.noext.name
+
+  @property
+  def noext(self):
+    """ '/home/a/bc.d' -> '/home/a/bc' """
+    return Path(os.path.splitext(self)[0])
+
+  @property
+  def ext(self):
+    """ '/home/a/bc.d' -> '.d' """
+    return Path(os.path.splitext(self)[1])
+
+  @property
+  def abspath(self):
+    """ './a/bc.d' -> '/home/a/bc.d'  """
+    return Path(os.path.abspath(self))
+
+  @property
+  def realpath(self):
+    """ Resolves symbolic links. """
+    return Path(os.path.realpath(self))
+
+  @property
+  def normpath(self):
+    """ '/home/x/.././a//bc.d' -> '/home/a/bc.d' """
+    return Path(os.path.normpath(self))
+
+  @property
+  def folder(self):
+    """ Returns the folder of this path. """
+    """ '/home/a/bc.d' -> '/home/a' """
+    """ '/home/a/' -> '/home/a' """
+    """ '/home/a' -> '/home' """
+    return Path(os.path.dirname(self))
+
+  @property
+  def exists(self):
+    """ Returns True if the path exists. """
+    return os.path.exists(self)
+
+  @property
+  def atime(self):
+    """ Returns last accessed time. """
+    return os.path.getatime(self)
+
+  @property
+  def mtime(self):
+    """ Returns last modified time. """
+    return os.path.getmtime(self)
+
+  @property
+  def ctime(self):
+    """ Returns last changed time. """
+    return os.path.getctime(self)
+
+  @classmethod
+  def supports_unicode(self):
+    """ Returns True if the system can handle Unicode file names. """
+    return os.path.supports_unicode_filenames()
+
+  def walk(self, **kwargs):
+    """ Returns a generator that walks through a directory tree. """
+    if "followlinks" in kwargs:
+      from sys import version_info as vi
+      if vi[0]*10+vi[1] < 26: # Only Python 2.6 or newer supports followlinks.
+        del kwargs["followlinks"]
+    return os.walk(self, **kwargs)
+
+  def mkdir(self, mode=0777):
+    """ Creates a directory, if it doesn't exist already. """
+    if not self.exists:
+      os.mkdir(self, mode)
+
+  def makedirs(self, mode=0777):
+    """ Like mkdir(), but also creates parent directories. """
+    if not self.exists:
+      os.makedirs(self, mode)
+
+  def remove(self):
+    """ Removes a file. """
+    os.remove(self)
+  rm = remove # Alias.
+
+  def rmdir(self):
+    """ Removes a directory. """
+    return os.rmdir(self)
+
+  def rmtree(self, noerrors=True):
+    """ Removes a directory tree. Ignores errors by default. """
+    return shutil.rmtree(self, ignore_errors=noerrors)
+
+  def copy(self, to):
+    shutil.copy(self, to)
+
+  def copytree(self, to):
+    """ Copies a directory tree to another path. """
+    shutil.copytree(self, to)
+
+  def move(self, to):
+    """ Moves a file or directory to another path. """
+    shutil.move(self, to)
+
+  def rename(self, to):
+    """ Renames a file or directory. May throw an OSError. """
+    os.rename(self, to)
+
+  def renames(self, to):
+    os.renames(self, to)
+
+  def glob(self, pattern):
+    from glob import glob
+    return map(Path, glob(unicode(self/pattern)))