initial import of the packaging package in the standard library
diff --git a/Lib/packaging/tests/LONG_DESC.txt b/Lib/packaging/tests/LONG_DESC.txt
new file mode 100644
index 0000000..2b4358a
--- /dev/null
+++ b/Lib/packaging/tests/LONG_DESC.txt
@@ -0,0 +1,44 @@
+CLVault
+=======
+
+CLVault uses Keyring to provide a command-line utility to safely store
+and retrieve passwords.
+
+Install it using pip or the setup.py script::
+
+    $ python setup.py install
+
+    $ pip install clvault
+
+Once it's installed, you will have three scripts installed in your
+Python scripts folder, you can use to list, store and retrieve passwords::
+
+    $ clvault-set blog
+    Set your password:
+    Set the associated username (can be blank): tarek
+    Set a description (can be blank): My blog password
+    Password set.
+
+    $ clvault-get blog
+    The username is "tarek"
+    The password has been copied in your clipboard
+
+    $ clvault-list
+    Registered services:
+    blog    My blog password
+
+
+*clvault-set* takes a service name then prompt you for a password, and some
+optional information about your service. The password is safely stored in
+a keyring while the description is saved in a ``.clvault`` file in your
+home directory. This file is created automatically the first time the command
+is used.
+
+*clvault-get* copies the password for a given service in your clipboard, and
+displays the associated user if any.
+
+*clvault-list* lists all registered services, with their description when
+given.
+
+
+Project page: http://bitbucket.org/tarek/clvault
diff --git a/Lib/packaging/tests/PKG-INFO b/Lib/packaging/tests/PKG-INFO
new file mode 100644
index 0000000..f48546e
--- /dev/null
+++ b/Lib/packaging/tests/PKG-INFO
@@ -0,0 +1,57 @@
+Metadata-Version: 1.2
+Name: CLVault
+Version: 0.5
+Summary: Command-Line utility to store and retrieve passwords
+Home-page: http://bitbucket.org/tarek/clvault
+Author: Tarek Ziade
+Author-email: tarek@ziade.org
+License: PSF
+Keywords: keyring,password,crypt
+Requires-Dist: foo; sys.platform == 'okook'
+Requires-Dist: bar; sys.platform == '%s'
+Platform: UNKNOWN
+Description: CLVault
+       |=======
+       |
+       |CLVault uses Keyring to provide a command-line utility to safely store
+       |and retrieve passwords.
+       |
+       |Install it using pip or the setup.py script::
+       |
+       |    $ python setup.py install
+       |
+       |    $ pip install clvault
+       |
+       |Once it's installed, you will have three scripts installed in your
+       |Python scripts folder, you can use to list, store and retrieve passwords::
+       |
+       |    $ clvault-set blog
+       |    Set your password:
+       |    Set the associated username (can be blank): tarek
+       |    Set a description (can be blank): My blog password
+       |    Password set.
+       |
+       |    $ clvault-get blog
+       |    The username is "tarek"
+       |    The password has been copied in your clipboard
+       |
+       |    $ clvault-list
+       |    Registered services:
+       |    blog    My blog password
+       |
+       |
+       |*clvault-set* takes a service name then prompt you for a password, and some
+       |optional information about your service. The password is safely stored in
+       |a keyring while the description is saved in a ``.clvault`` file in your
+       |home directory. This file is created automatically the first time the command
+       |is used.
+       |
+       |*clvault-get* copies the password for a given service in your clipboard, and
+       |displays the associated user if any.
+       |
+       |*clvault-list* lists all registered services, with their description when
+       |given.
+       |
+       |
+       |Project page: http://bitbucket.org/tarek/clvault
+       |
diff --git a/Lib/packaging/tests/SETUPTOOLS-PKG-INFO b/Lib/packaging/tests/SETUPTOOLS-PKG-INFO
new file mode 100644
index 0000000..dff8d00
--- /dev/null
+++ b/Lib/packaging/tests/SETUPTOOLS-PKG-INFO
@@ -0,0 +1,182 @@
+Metadata-Version: 1.0
+Name: setuptools
+Version: 0.6c9
+Summary: Download, build, install, upgrade, and uninstall Python packages -- easily!
+Home-page: http://pypi.python.org/pypi/setuptools
+Author: Phillip J. Eby
+Author-email: distutils-sig@python.org
+License: PSF or ZPL
+Description: ===============================
+        Installing and Using Setuptools
+        ===============================
+
+        .. contents:: **Table of Contents**
+
+
+        -------------------------
+        Installation Instructions
+        -------------------------
+
+        Windows
+        =======
+
+        Install setuptools using the provided ``.exe`` installer.  If you've previously
+        installed older versions of setuptools, please delete all ``setuptools*.egg``
+        and ``setuptools.pth`` files from your system's ``site-packages`` directory
+        (and any other ``sys.path`` directories) FIRST.
+
+        If you are upgrading a previous version of setuptools that was installed using
+        an ``.exe`` installer, please be sure to also *uninstall that older version*
+        via your system's "Add/Remove Programs" feature, BEFORE installing the newer
+        version.
+
+        Once installation is complete, you will find an ``easy_install.exe`` program in
+        your Python ``Scripts`` subdirectory.  Be sure to add this directory to your
+        ``PATH`` environment variable, if you haven't already done so.
+
+
+        RPM-Based Systems
+        =================
+
+        Install setuptools using the provided source RPM.  The included ``.spec`` file
+        assumes you are installing using the default ``python`` executable, and is not
+        specific to a particular Python version.  The ``easy_install`` executable will
+        be installed to a system ``bin`` directory such as ``/usr/bin``.
+
+        If you wish to install to a location other than the default Python
+        installation's default ``site-packages`` directory (and ``$prefix/bin`` for
+        scripts), please use the ``.egg``-based installation approach described in the
+        following section.
+
+
+        Cygwin, Mac OS X, Linux, Other
+        ==============================
+
+        1. Download the appropriate egg for your version of Python (e.g.
+        ``setuptools-0.6c9-py2.4.egg``).  Do NOT rename it.
+
+        2. Run it as if it were a shell script, e.g. ``sh setuptools-0.6c9-py2.4.egg``.
+        Setuptools will install itself using the matching version of Python (e.g.
+        ``python2.4``), and will place the ``easy_install`` executable in the
+        default location for installing Python scripts (as determined by the
+        standard distutils configuration files, or by the Python installation).
+
+        If you want to install setuptools to somewhere other than ``site-packages`` or
+        your default distutils installation locations for libraries and scripts, you
+        may include EasyInstall command-line options such as ``--prefix``,
+        ``--install-dir``, and so on, following the ``.egg`` filename on the same
+        command line.  For example::
+
+        sh setuptools-0.6c9-py2.4.egg --prefix=~
+
+        You can use ``--help`` to get a full options list, but we recommend consulting
+        the `EasyInstall manual`_ for detailed instructions, especially `the section
+        on custom installation locations`_.
+
+        .. _EasyInstall manual: http://peak.telecommunity.com/DevCenter/EasyInstall
+        .. _the section on custom installation locations: http://peak.telecommunity.com/DevCenter/EasyInstall#custom-installation-locations
+
+
+        Cygwin Note
+        -----------
+
+        If you are trying to install setuptools for the **Windows** version of Python
+        (as opposed to the Cygwin version that lives in ``/usr/bin``), you must make
+        sure that an appropriate executable (``python2.3``, ``python2.4``, or
+        ``python2.5``) is on your **Cygwin** ``PATH`` when invoking the egg.  For
+        example, doing the following at a Cygwin bash prompt will install setuptools
+        for the **Windows** Python found at ``C:\\Python24``::
+
+        ln -s /cygdrive/c/Python24/python.exe python2.4
+        PATH=.:$PATH sh setuptools-0.6c9-py2.4.egg
+        rm python2.4
+
+
+        Downloads
+        =========
+
+        All setuptools downloads can be found at `the project's home page in the Python
+        Package Index`_.  Scroll to the very bottom of the page to find the links.
+
+        .. _the project's home page in the Python Package Index: http://pypi.python.org/pypi/setuptools
+
+        In addition to the PyPI downloads, the development version of ``setuptools``
+        is available from the `Python SVN sandbox`_, and in-development versions of the
+        `0.6 branch`_ are available as well.
+
+        .. _0.6 branch: http://svn.python.org/projects/sandbox/branches/setuptools-0.6/#egg=setuptools-dev06
+
+        .. _Python SVN sandbox: http://svn.python.org/projects/sandbox/trunk/setuptools/#egg=setuptools-dev
+
+        --------------------------------
+        Using Setuptools and EasyInstall
+        --------------------------------
+
+        Here are some of the available manuals, tutorials, and other resources for
+        learning about Setuptools, Python Eggs, and EasyInstall:
+
+        * `The EasyInstall user's guide and reference manual`_
+        * `The setuptools Developer's Guide`_
+        * `The pkg_resources API reference`_
+        * `Package Compatibility Notes`_ (user-maintained)
+        * `The Internal Structure of Python Eggs`_
+
+        Questions, comments, and bug reports should be directed to the `distutils-sig
+        mailing list`_.  If you have written (or know of) any tutorials, documentation,
+        plug-ins, or other resources for setuptools users, please let us know about
+        them there, so this reference list can be updated.  If you have working,
+        *tested* patches to correct problems or add features, you may submit them to
+        the `setuptools bug tracker`_.
+
+        .. _setuptools bug tracker: http://bugs.python.org/setuptools/
+        .. _Package Compatibility Notes: http://peak.telecommunity.com/DevCenter/PackageNotes
+        .. _The Internal Structure of Python Eggs: http://peak.telecommunity.com/DevCenter/EggFormats
+        .. _The setuptools Developer's Guide: http://peak.telecommunity.com/DevCenter/setuptools
+        .. _The pkg_resources API reference: http://peak.telecommunity.com/DevCenter/PkgResources
+        .. _The EasyInstall user's guide and reference manual: http://peak.telecommunity.com/DevCenter/EasyInstall
+        .. _distutils-sig mailing list: http://mail.python.org/pipermail/distutils-sig/
+
+
+        -------
+        Credits
+        -------
+
+        * The original design for the ``.egg`` format and the ``pkg_resources`` API was
+        co-created by Phillip Eby and Bob Ippolito.  Bob also implemented the first
+        version of ``pkg_resources``, and supplied the OS X operating system version
+        compatibility algorithm.
+
+        * Ian Bicking implemented many early "creature comfort" features of
+        easy_install, including support for downloading via Sourceforge and
+        Subversion repositories.  Ian's comments on the Web-SIG about WSGI
+        application deployment also inspired the concept of "entry points" in eggs,
+        and he has given talks at PyCon and elsewhere to inform and educate the
+        community about eggs and setuptools.
+
+        * Jim Fulton contributed time and effort to build automated tests of various
+        aspects of ``easy_install``, and supplied the doctests for the command-line
+        ``.exe`` wrappers on Windows.
+
+        * Phillip J. Eby is the principal author and maintainer of setuptools, and
+        first proposed the idea of an importable binary distribution format for
+        Python application plug-ins.
+
+        * Significant parts of the implementation of setuptools were funded by the Open
+        Source Applications Foundation, to provide a plug-in infrastructure for the
+        Chandler PIM application.  In addition, many OSAF staffers (such as Mike
+        "Code Bear" Taylor) contributed their time and stress as guinea pigs for the
+        use of eggs and setuptools, even before eggs were "cool".  (Thanks, guys!)
+
+
+Keywords: CPAN PyPI distutils eggs package management
+Platform: UNKNOWN
+Classifier: Development Status :: 3 - Alpha
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Python Software Foundation License
+Classifier: License :: OSI Approved :: Zope Public License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Archiving :: Packaging
+Classifier: Topic :: System :: Systems Administration
+Classifier: Topic :: Utilities
diff --git a/Lib/packaging/tests/SETUPTOOLS-PKG-INFO2 b/Lib/packaging/tests/SETUPTOOLS-PKG-INFO2
new file mode 100644
index 0000000..4b3906a
--- /dev/null
+++ b/Lib/packaging/tests/SETUPTOOLS-PKG-INFO2
@@ -0,0 +1,183 @@
+Metadata-Version: 1.1
+Name: setuptools
+Version: 0.6c9
+Summary: Download, build, install, upgrade, and uninstall Python packages -- easily!
+Home-page: http://pypi.python.org/pypi/setuptools
+Author: Phillip J. Eby
+Author-email: distutils-sig@python.org
+License: PSF or ZPL
+Description: ===============================
+        Installing and Using Setuptools
+        ===============================
+
+        .. contents:: **Table of Contents**
+
+
+        -------------------------
+        Installation Instructions
+        -------------------------
+
+        Windows
+        =======
+
+        Install setuptools using the provided ``.exe`` installer.  If you've previously
+        installed older versions of setuptools, please delete all ``setuptools*.egg``
+        and ``setuptools.pth`` files from your system's ``site-packages`` directory
+        (and any other ``sys.path`` directories) FIRST.
+
+        If you are upgrading a previous version of setuptools that was installed using
+        an ``.exe`` installer, please be sure to also *uninstall that older version*
+        via your system's "Add/Remove Programs" feature, BEFORE installing the newer
+        version.
+
+        Once installation is complete, you will find an ``easy_install.exe`` program in
+        your Python ``Scripts`` subdirectory.  Be sure to add this directory to your
+        ``PATH`` environment variable, if you haven't already done so.
+
+
+        RPM-Based Systems
+        =================
+
+        Install setuptools using the provided source RPM.  The included ``.spec`` file
+        assumes you are installing using the default ``python`` executable, and is not
+        specific to a particular Python version.  The ``easy_install`` executable will
+        be installed to a system ``bin`` directory such as ``/usr/bin``.
+
+        If you wish to install to a location other than the default Python
+        installation's default ``site-packages`` directory (and ``$prefix/bin`` for
+        scripts), please use the ``.egg``-based installation approach described in the
+        following section.
+
+
+        Cygwin, Mac OS X, Linux, Other
+        ==============================
+
+        1. Download the appropriate egg for your version of Python (e.g.
+        ``setuptools-0.6c9-py2.4.egg``).  Do NOT rename it.
+
+        2. Run it as if it were a shell script, e.g. ``sh setuptools-0.6c9-py2.4.egg``.
+        Setuptools will install itself using the matching version of Python (e.g.
+        ``python2.4``), and will place the ``easy_install`` executable in the
+        default location for installing Python scripts (as determined by the
+        standard distutils configuration files, or by the Python installation).
+
+        If you want to install setuptools to somewhere other than ``site-packages`` or
+        your default distutils installation locations for libraries and scripts, you
+        may include EasyInstall command-line options such as ``--prefix``,
+        ``--install-dir``, and so on, following the ``.egg`` filename on the same
+        command line.  For example::
+
+        sh setuptools-0.6c9-py2.4.egg --prefix=~
+
+        You can use ``--help`` to get a full options list, but we recommend consulting
+        the `EasyInstall manual`_ for detailed instructions, especially `the section
+        on custom installation locations`_.
+
+        .. _EasyInstall manual: http://peak.telecommunity.com/DevCenter/EasyInstall
+        .. _the section on custom installation locations: http://peak.telecommunity.com/DevCenter/EasyInstall#custom-installation-locations
+
+
+        Cygwin Note
+        -----------
+
+        If you are trying to install setuptools for the **Windows** version of Python
+        (as opposed to the Cygwin version that lives in ``/usr/bin``), you must make
+        sure that an appropriate executable (``python2.3``, ``python2.4``, or
+        ``python2.5``) is on your **Cygwin** ``PATH`` when invoking the egg.  For
+        example, doing the following at a Cygwin bash prompt will install setuptools
+        for the **Windows** Python found at ``C:\\Python24``::
+
+        ln -s /cygdrive/c/Python24/python.exe python2.4
+        PATH=.:$PATH sh setuptools-0.6c9-py2.4.egg
+        rm python2.4
+
+
+        Downloads
+        =========
+
+        All setuptools downloads can be found at `the project's home page in the Python
+        Package Index`_.  Scroll to the very bottom of the page to find the links.
+
+        .. _the project's home page in the Python Package Index: http://pypi.python.org/pypi/setuptools
+
+        In addition to the PyPI downloads, the development version of ``setuptools``
+        is available from the `Python SVN sandbox`_, and in-development versions of the
+        `0.6 branch`_ are available as well.
+
+        .. _0.6 branch: http://svn.python.org/projects/sandbox/branches/setuptools-0.6/#egg=setuptools-dev06
+
+        .. _Python SVN sandbox: http://svn.python.org/projects/sandbox/trunk/setuptools/#egg=setuptools-dev
+
+        --------------------------------
+        Using Setuptools and EasyInstall
+        --------------------------------
+
+        Here are some of the available manuals, tutorials, and other resources for
+        learning about Setuptools, Python Eggs, and EasyInstall:
+
+        * `The EasyInstall user's guide and reference manual`_
+        * `The setuptools Developer's Guide`_
+        * `The pkg_resources API reference`_
+        * `Package Compatibility Notes`_ (user-maintained)
+        * `The Internal Structure of Python Eggs`_
+
+        Questions, comments, and bug reports should be directed to the `distutils-sig
+        mailing list`_.  If you have written (or know of) any tutorials, documentation,
+        plug-ins, or other resources for setuptools users, please let us know about
+        them there, so this reference list can be updated.  If you have working,
+        *tested* patches to correct problems or add features, you may submit them to
+        the `setuptools bug tracker`_.
+
+        .. _setuptools bug tracker: http://bugs.python.org/setuptools/
+        .. _Package Compatibility Notes: http://peak.telecommunity.com/DevCenter/PackageNotes
+        .. _The Internal Structure of Python Eggs: http://peak.telecommunity.com/DevCenter/EggFormats
+        .. _The setuptools Developer's Guide: http://peak.telecommunity.com/DevCenter/setuptools
+        .. _The pkg_resources API reference: http://peak.telecommunity.com/DevCenter/PkgResources
+        .. _The EasyInstall user's guide and reference manual: http://peak.telecommunity.com/DevCenter/EasyInstall
+        .. _distutils-sig mailing list: http://mail.python.org/pipermail/distutils-sig/
+
+
+        -------
+        Credits
+        -------
+
+        * The original design for the ``.egg`` format and the ``pkg_resources`` API was
+        co-created by Phillip Eby and Bob Ippolito.  Bob also implemented the first
+        version of ``pkg_resources``, and supplied the OS X operating system version
+        compatibility algorithm.
+
+        * Ian Bicking implemented many early "creature comfort" features of
+        easy_install, including support for downloading via Sourceforge and
+        Subversion repositories.  Ian's comments on the Web-SIG about WSGI
+        application deployment also inspired the concept of "entry points" in eggs,
+        and he has given talks at PyCon and elsewhere to inform and educate the
+        community about eggs and setuptools.
+
+        * Jim Fulton contributed time and effort to build automated tests of various
+        aspects of ``easy_install``, and supplied the doctests for the command-line
+        ``.exe`` wrappers on Windows.
+
+        * Phillip J. Eby is the principal author and maintainer of setuptools, and
+        first proposed the idea of an importable binary distribution format for
+        Python application plug-ins.
+
+        * Significant parts of the implementation of setuptools were funded by the Open
+        Source Applications Foundation, to provide a plug-in infrastructure for the
+        Chandler PIM application.  In addition, many OSAF staffers (such as Mike
+        "Code Bear" Taylor) contributed their time and stress as guinea pigs for the
+        use of eggs and setuptools, even before eggs were "cool".  (Thanks, guys!)
+
+
+Keywords: CPAN PyPI distutils eggs package management
+Platform: UNKNOWN
+Classifier: Development Status :: 3 - Alpha
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Python Software Foundation License
+Classifier: License :: OSI Approved :: Zope Public License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Archiving :: Packaging
+Classifier: Topic :: System :: Systems Administration
+Classifier: Topic :: Utilities
+Requires: Foo
diff --git a/Lib/packaging/tests/__init__.py b/Lib/packaging/tests/__init__.py
new file mode 100644
index 0000000..0b0e3c5
--- /dev/null
+++ b/Lib/packaging/tests/__init__.py
@@ -0,0 +1,133 @@
+"""Test suite for packaging.
+
+This test suite consists of a collection of test modules in the
+packaging.tests package.  Each test module has a name starting with
+'test' and contains a function test_suite().  The function is expected
+to return an initialized unittest.TestSuite instance.
+
+Utility code is included in packaging.tests.support.
+"""
+
+# Put this text back for the backport
+#Always import unittest from this module, it will be the right version
+#(standard library unittest for 3.2 and higher, third-party unittest2
+#elease for older versions).
+
+import os
+import sys
+import unittest
+from test.support import TESTFN
+
+# XXX move helpers to support, add tests for them, remove things that
+# duplicate test.support (or keep them for the backport; needs thinking)
+
+here = os.path.dirname(__file__) or os.curdir
+verbose = 1
+
+def test_suite():
+    suite = unittest.TestSuite()
+    for fn in os.listdir(here):
+        if fn.startswith("test") and fn.endswith(".py"):
+            modname = "packaging.tests." + fn[:-3]
+            __import__(modname)
+            module = sys.modules[modname]
+            suite.addTest(module.test_suite())
+    return suite
+
+
+class Error(Exception):
+    """Base class for regression test exceptions."""
+
+
+class TestFailed(Error):
+    """Test failed."""
+
+
+class BasicTestRunner:
+    def run(self, test):
+        result = unittest.TestResult()
+        test(result)
+        return result
+
+
+def _run_suite(suite, verbose_=1):
+    """Run tests from a unittest.TestSuite-derived class."""
+    global verbose
+    verbose = verbose_
+    if verbose_:
+        runner = unittest.TextTestRunner(sys.stdout, verbosity=2)
+    else:
+        runner = BasicTestRunner()
+
+    result = runner.run(suite)
+    if not result.wasSuccessful():
+        if len(result.errors) == 1 and not result.failures:
+            err = result.errors[0][1]
+        elif len(result.failures) == 1 and not result.errors:
+            err = result.failures[0][1]
+        else:
+            err = "errors occurred; run in verbose mode for details"
+        raise TestFailed(err)
+
+
+def run_unittest(classes, verbose_=1):
+    """Run tests from unittest.TestCase-derived classes.
+
+    Originally extracted from stdlib test.test_support and modified to
+    support unittest2.
+    """
+    valid_types = (unittest.TestSuite, unittest.TestCase)
+    suite = unittest.TestSuite()
+    for cls in classes:
+        if isinstance(cls, str):
+            if cls in sys.modules:
+                suite.addTest(unittest.findTestCases(sys.modules[cls]))
+            else:
+                raise ValueError("str arguments must be keys in sys.modules")
+        elif isinstance(cls, valid_types):
+            suite.addTest(cls)
+        else:
+            suite.addTest(unittest.makeSuite(cls))
+    _run_suite(suite, verbose_)
+
+
+def reap_children():
+    """Use this function at the end of test_main() whenever sub-processes
+    are started.  This will help ensure that no extra children (zombies)
+    stick around to hog resources and create problems when looking
+    for refleaks.
+
+    Extracted from stdlib test.support.
+    """
+
+    # Reap all our dead child processes so we don't leave zombies around.
+    # These hog resources and might be causing some of the buildbots to die.
+    if hasattr(os, 'waitpid'):
+        any_process = -1
+        while True:
+            try:
+                # This will raise an exception on Windows.  That's ok.
+                pid, status = os.waitpid(any_process, os.WNOHANG)
+                if pid == 0:
+                    break
+            except:
+                break
+
+
+def captured_stdout(func, *args, **kw):
+    import io
+    orig_stdout = getattr(sys, 'stdout')
+    setattr(sys, 'stdout', io.StringIO())
+    try:
+        res = func(*args, **kw)
+        sys.stdout.seek(0)
+        return res, sys.stdout.read()
+    finally:
+        setattr(sys, 'stdout', orig_stdout)
+
+
+def unload(name):
+    try:
+        del sys.modules[name]
+    except KeyError:
+        pass
diff --git a/Lib/packaging/tests/__main__.py b/Lib/packaging/tests/__main__.py
new file mode 100644
index 0000000..68ee229
--- /dev/null
+++ b/Lib/packaging/tests/__main__.py
@@ -0,0 +1,20 @@
+"""Packaging test suite runner."""
+
+# Ripped from importlib tests, thanks Brett!
+
+import os
+import sys
+import unittest
+from test.support import run_unittest, reap_children
+
+
+def test_main():
+    start_dir = os.path.dirname(__file__)
+    top_dir = os.path.dirname(os.path.dirname(start_dir))
+    test_loader = unittest.TestLoader()
+    run_unittest(test_loader.discover(start_dir, top_level_dir=top_dir))
+    reap_children()
+
+
+if __name__ == '__main__':
+    test_main()
diff --git a/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/INSTALLER b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/INSTALLER
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/INSTALLER
diff --git a/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/METADATA b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/METADATA
new file mode 100644
index 0000000..65e839a
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/METADATA
@@ -0,0 +1,4 @@
+Metadata-version: 1.2
+Name: babar
+Version: 0.1
+Author: FELD Boris
\ No newline at end of file
diff --git a/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RECORD b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RECORD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RECORD
diff --git a/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/REQUESTED b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/REQUESTED
diff --git a/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RESOURCES b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RESOURCES
new file mode 100644
index 0000000..5d0da49
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RESOURCES
@@ -0,0 +1,2 @@
+babar.png,babar.png
+babar.cfg,babar.cfg
\ No newline at end of file
diff --git a/Lib/packaging/tests/fake_dists/babar.cfg b/Lib/packaging/tests/fake_dists/babar.cfg
new file mode 100644
index 0000000..ecd6efe
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar.cfg
@@ -0,0 +1 @@
+Config
\ No newline at end of file
diff --git a/Lib/packaging/tests/fake_dists/babar.png b/Lib/packaging/tests/fake_dists/babar.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar.png
diff --git a/Lib/packaging/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO b/Lib/packaging/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO
new file mode 100644
index 0000000..a176dfd
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO
@@ -0,0 +1,6 @@
+Metadata-Version: 1.2
+Name: bacon
+Version: 0.1
+Provides-Dist: truffles (2.0)
+Provides-Dist: bacon (0.1)
+Obsoletes-Dist: truffles (>=0.9,<=1.5)
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO
new file mode 100644
index 0000000..a7e118a
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO
@@ -0,0 +1,18 @@
+Metadata-Version: 1.0
+Name: banana
+Version: 0.4
+Summary: A yellow fruit
+Home-page: http://en.wikipedia.org/wiki/Banana
+Author: Josip Djolonga
+Author-email: foo@nbar.com
+License: BSD
+Description: A fruit
+Keywords: foo bar
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Science/Research
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Scientific/Engineering :: GIS
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt
new file mode 100644
index 0000000..5d3e5f6
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt
@@ -0,0 +1,3 @@
+
+      # -*- Entry points: -*-
+      
\ No newline at end of file
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt
new file mode 100644
index 0000000..4354305
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt
@@ -0,0 +1,6 @@
+# this should be ignored
+
+strawberry >=0.5
+
+[section ignored]
+foo ==0.5
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt
diff --git a/Lib/packaging/tests/fake_dists/cheese-2.0.2.egg-info b/Lib/packaging/tests/fake_dists/cheese-2.0.2.egg-info
new file mode 100644
index 0000000..27cbe30
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/cheese-2.0.2.egg-info
@@ -0,0 +1,5 @@
+Metadata-Version: 1.2
+Name: cheese
+Version: 2.0.2
+Provides-Dist: truffles (1.0.2)
+Obsoletes-Dist: truffles (!=1.2,<=2.0)
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA
new file mode 100644
index 0000000..418929e
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA
@@ -0,0 +1,9 @@
+Metadata-Version: 1.2
+Name: choxie
+Version: 2.0.0.9
+Summary: Chocolate with a kick!
+Requires-Dist: towel-stuff (0.1)
+Requires-Dist: nut
+Provides-Dist: truffles (1.0)
+Obsoletes-Dist: truffles (<=0.8,>=0.5)
+Obsoletes-Dist: truffles (<=0.9,>=0.6)
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py
new file mode 100644
index 0000000..40a96af
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py
new file mode 100644
index 0000000..c4027f3
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+from towel_stuff import Towel
+
+class Chocolate(object):
+    """A piece of chocolate."""
+
+    def wrap_with_towel(self):
+        towel = Towel()
+        towel.wrap(self)
+        return towel
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/truffles.py b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/truffles.py
new file mode 100644
index 0000000..342b8ea
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/truffles.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+from choxie.chocolate import Chocolate
+
+class Truffle(Chocolate):
+    """A truffle."""
diff --git a/Lib/packaging/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO b/Lib/packaging/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO
new file mode 100644
index 0000000..499a083
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO
@@ -0,0 +1,5 @@
+Metadata-Version: 1.2
+Name: coconuts-aster
+Version: 10.3
+Provides-Dist: strawberry (0.6)
+Provides-Dist: banana (0.4)
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/INSTALLER b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/INSTALLER
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/INSTALLER
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/METADATA b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/METADATA
new file mode 100644
index 0000000..0b99f52
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/METADATA
@@ -0,0 +1,5 @@
+Metadata-Version: 1.2
+Name: grammar
+Version: 1.0a4
+Requires-Dist: truffles (>=1.2)
+Author: Sherlock Holmes
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/RECORD b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/RECORD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/RECORD
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/REQUESTED b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/REQUESTED
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/__init__.py b/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/__init__.py
new file mode 100644
index 0000000..40a96af
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/utils.py b/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/utils.py
new file mode 100644
index 0000000..66ba796
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/utils.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+from random import randint
+
+def is_valid_grammar(sentence):
+    if randint(0, 10) < 2:
+        return False
+    else:
+        return True
diff --git a/Lib/packaging/tests/fake_dists/nut-funkyversion.egg-info b/Lib/packaging/tests/fake_dists/nut-funkyversion.egg-info
new file mode 100644
index 0000000..0c58ec1
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/nut-funkyversion.egg-info
@@ -0,0 +1,3 @@
+Metadata-Version: 1.2
+Name: nut
+Version: funkyversion
diff --git a/Lib/packaging/tests/fake_dists/strawberry-0.6.egg b/Lib/packaging/tests/fake_dists/strawberry-0.6.egg
new file mode 100644
index 0000000..6d160e8
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/strawberry-0.6.egg
Binary files differ
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/INSTALLER b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/INSTALLER
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/INSTALLER
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA
new file mode 100644
index 0000000..ca46d0a
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA
@@ -0,0 +1,7 @@
+Metadata-Version: 1.2
+Name: towel-stuff
+Version: 0.1
+Provides-Dist: truffles (1.1.2)
+Provides-Dist: towel-stuff (0.1)
+Obsoletes-Dist: truffles (!=0.8,<1.0)
+Requires-Dist: bacon (<=0.2)
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/RECORD b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/RECORD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/RECORD
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/REQUESTED b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/REQUESTED
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py b/Lib/packaging/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py
new file mode 100644
index 0000000..191f895
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+
+class Towel(object):
+    """A towel, that one should never be without."""
+
+    def __init__(self, color='tie-dye'):
+        self.color = color
+        self.wrapped_obj = None
+
+    def wrap(self, obj):
+        """Wrap an object up in our towel."""
+        self.wrapped_obj = obj
+
+    def unwrap(self):
+        """Unwrap whatever is in our towel and return whatever it is."""
+        obj = self.wrapped_obj
+        self.wrapped_obj = None
+        return obj
diff --git a/Lib/packaging/tests/fake_dists/truffles-5.0.egg-info b/Lib/packaging/tests/fake_dists/truffles-5.0.egg-info
new file mode 100644
index 0000000..45f0cf8
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/truffles-5.0.egg-info
@@ -0,0 +1,3 @@
+Metadata-Version: 1.2
+Name: truffles
+Version: 5.0
diff --git a/Lib/packaging/tests/fixer/__init__.py b/Lib/packaging/tests/fixer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fixer/__init__.py
diff --git a/Lib/packaging/tests/fixer/fix_idioms.py b/Lib/packaging/tests/fixer/fix_idioms.py
new file mode 100644
index 0000000..64f5ea0
--- /dev/null
+++ b/Lib/packaging/tests/fixer/fix_idioms.py
@@ -0,0 +1,134 @@
+"""Adjust some old Python 2 idioms to their modern counterparts.
+
+* Change some type comparisons to isinstance() calls:
+    type(x) == T -> isinstance(x, T)
+    type(x) is T -> isinstance(x, T)
+    type(x) != T -> not isinstance(x, T)
+    type(x) is not T -> not isinstance(x, T)
+
+* Change "while 1:" into "while True:".
+
+* Change both
+
+    v = list(EXPR)
+    v.sort()
+    foo(v)
+
+and the more general
+
+    v = EXPR
+    v.sort()
+    foo(v)
+
+into
+
+    v = sorted(EXPR)
+    foo(v)
+"""
+# Author: Jacques Frechet, Collin Winter
+
+# Local imports
+from lib2to3 import fixer_base
+from lib2to3.fixer_util import Call, Comma, Name, Node, syms
+
+CMP = "(n='!=' | '==' | 'is' | n=comp_op< 'is' 'not' >)"
+TYPE = "power< 'type' trailer< '(' x=any ')' > >"
+
+class FixIdioms(fixer_base.BaseFix):
+
+    explicit = False # The user must ask for this fixer
+
+    PATTERN = r"""
+        isinstance=comparison< %s %s T=any >
+        |
+        isinstance=comparison< T=any %s %s >
+        |
+        while_stmt< 'while' while='1' ':' any+ >
+        |
+        sorted=any<
+            any*
+            simple_stmt<
+              expr_stmt< id1=any '='
+                         power< list='list' trailer< '(' (not arglist<any+>) any ')' > >
+              >
+              '\n'
+            >
+            sort=
+            simple_stmt<
+              power< id2=any
+                     trailer< '.' 'sort' > trailer< '(' ')' >
+              >
+              '\n'
+            >
+            next=any*
+        >
+        |
+        sorted=any<
+            any*
+            simple_stmt< expr_stmt< id1=any '=' expr=any > '\n' >
+            sort=
+            simple_stmt<
+              power< id2=any
+                     trailer< '.' 'sort' > trailer< '(' ')' >
+              >
+              '\n'
+            >
+            next=any*
+        >
+    """ % (TYPE, CMP, CMP, TYPE)
+
+    def match(self, node):
+        r = super(FixIdioms, self).match(node)
+        # If we've matched one of the sort/sorted subpatterns above, we
+        # want to reject matches where the initial assignment and the
+        # subsequent .sort() call involve different identifiers.
+        if r and "sorted" in r:
+            if r["id1"] == r["id2"]:
+                return r
+            return None
+        return r
+
+    def transform(self, node, results):
+        if "isinstance" in results:
+            return self.transform_isinstance(node, results)
+        elif "while" in results:
+            return self.transform_while(node, results)
+        elif "sorted" in results:
+            return self.transform_sort(node, results)
+        else:
+            raise RuntimeError("Invalid match")
+
+    def transform_isinstance(self, node, results):
+        x = results["x"].clone() # The thing inside of type()
+        T = results["T"].clone() # The type being compared against
+        x.prefix = ""
+        T.prefix = " "
+        test = Call(Name("isinstance"), [x, Comma(), T])
+        if "n" in results:
+            test.prefix = " "
+            test = Node(syms.not_test, [Name("not"), test])
+        test.prefix = node.prefix
+        return test
+
+    def transform_while(self, node, results):
+        one = results["while"]
+        one.replace(Name("True", prefix=one.prefix))
+
+    def transform_sort(self, node, results):
+        sort_stmt = results["sort"]
+        next_stmt = results["next"]
+        list_call = results.get("list")
+        simple_expr = results.get("expr")
+
+        if list_call:
+            list_call.replace(Name("sorted", prefix=list_call.prefix))
+        elif simple_expr:
+            new = simple_expr.clone()
+            new.prefix = ""
+            simple_expr.replace(Call(Name("sorted"), [new],
+                                     prefix=simple_expr.prefix))
+        else:
+            raise RuntimeError("should not have reached here")
+        sort_stmt.remove()
+        if next_stmt:
+            next_stmt[0].prefix = sort_stmt._prefix
diff --git a/Lib/packaging/tests/pypi_server.py b/Lib/packaging/tests/pypi_server.py
new file mode 100644
index 0000000..cc5fcca
--- /dev/null
+++ b/Lib/packaging/tests/pypi_server.py
@@ -0,0 +1,444 @@
+"""Mock PyPI Server implementation, to use in tests.
+
+This module also provides a simple test case to extend if you need to use
+the PyPIServer all along your test case. Be sure to read the documentation
+before any use.
+
+XXX TODO:
+
+The mock server can handle simple HTTP request (to simulate a simple index) or
+XMLRPC requests, over HTTP. Both does not have the same intergface to deal
+with, and I think it's a pain.
+
+A good idea could be to re-think a bit the way dstributions are handled in the
+mock server. As it should return malformed HTML pages, we need to keep the
+static behavior.
+
+I think of something like that:
+
+    >>> server = PyPIMockServer()
+    >>> server.startHTTP()
+    >>> server.startXMLRPC()
+
+Then, the server must have only one port to rely on, eg.
+
+    >>> server.fulladress()
+    "http://ip:port/"
+
+It could be simple to have one HTTP server, relaying the requests to the two
+implementations (static HTTP and XMLRPC over HTTP).
+"""
+
+import os
+import queue
+import select
+import socket
+import threading
+import socketserver
+from functools import wraps
+from http.server import HTTPServer, SimpleHTTPRequestHandler
+from xmlrpc.server import SimpleXMLRPCServer
+
+from packaging.tests import unittest
+
+PYPI_DEFAULT_STATIC_PATH = os.path.join(
+    os.path.dirname(os.path.abspath(__file__)), 'pypiserver')
+
+
+def use_xmlrpc_server(*server_args, **server_kwargs):
+    server_kwargs['serve_xmlrpc'] = True
+    return use_pypi_server(*server_args, **server_kwargs)
+
+
+def use_http_server(*server_args, **server_kwargs):
+    server_kwargs['serve_xmlrpc'] = False
+    return use_pypi_server(*server_args, **server_kwargs)
+
+
+def use_pypi_server(*server_args, **server_kwargs):
+    """Decorator to make use of the PyPIServer for test methods,
+    just when needed, and not for the entire duration of the testcase.
+    """
+    def wrapper(func):
+        @wraps(func)
+        def wrapped(*args, **kwargs):
+            server = PyPIServer(*server_args, **server_kwargs)
+            server.start()
+            try:
+                func(server=server, *args, **kwargs)
+            finally:
+                server.stop()
+        return wrapped
+    return wrapper
+
+
+class PyPIServerTestCase(unittest.TestCase):
+
+    def setUp(self):
+        super(PyPIServerTestCase, self).setUp()
+        self.pypi = PyPIServer()
+        self.pypi.start()
+        self.addCleanup(self.pypi.stop)
+
+
+class PyPIServer(threading.Thread):
+    """PyPI Mocked server.
+    Provides a mocked version of the PyPI API's, to ease tests.
+
+    Support serving static content and serving previously given text.
+    """
+
+    def __init__(self, test_static_path=None,
+                 static_filesystem_paths=["default"],
+                 static_uri_paths=["simple", "packages"], serve_xmlrpc=False):
+        """Initialize the server.
+
+        Default behavior is to start the HTTP server. You can either start the
+        xmlrpc server by setting xmlrpc to True. Caution: Only one server will
+        be started.
+
+        static_uri_paths and static_base_path are parameters used to provides
+        respectively the http_paths to serve statically, and where to find the
+        matching files on the filesystem.
+        """
+        # we want to launch the server in a new dedicated thread, to not freeze
+        # tests.
+        threading.Thread.__init__(self)
+        self._run = True
+        self._serve_xmlrpc = serve_xmlrpc
+
+        #TODO allow to serve XMLRPC and HTTP static files at the same time.
+        if not self._serve_xmlrpc:
+            self.server = HTTPServer(('127.0.0.1', 0), PyPIRequestHandler)
+            self.server.RequestHandlerClass.pypi_server = self
+
+            self.request_queue = queue.Queue()
+            self._requests = []
+            self.default_response_status = 404
+            self.default_response_headers = [('Content-type', 'text/plain')]
+            self.default_response_data = "The page does not exists"
+
+            # initialize static paths / filesystems
+            self.static_uri_paths = static_uri_paths
+
+            # append the static paths defined locally
+            if test_static_path is not None:
+                static_filesystem_paths.append(test_static_path)
+            self.static_filesystem_paths = [
+                PYPI_DEFAULT_STATIC_PATH + "/" + path
+                for path in static_filesystem_paths]
+        else:
+            # XMLRPC server
+            self.server = PyPIXMLRPCServer(('127.0.0.1', 0))
+            self.xmlrpc = XMLRPCMockIndex()
+            # register the xmlrpc methods
+            self.server.register_introspection_functions()
+            self.server.register_instance(self.xmlrpc)
+
+        self.address = (self.server.server_name, self.server.server_port)
+        # to not have unwanted outputs.
+        self.server.RequestHandlerClass.log_request = lambda *_: None
+
+    def run(self):
+        # loop because we can't stop it otherwise, for python < 2.6
+        while self._run:
+            r, w, e = select.select([self.server], [], [], 0.5)
+            if r:
+                self.server.handle_request()
+
+    def stop(self):
+        """self shutdown is not supported for python < 2.6"""
+        self._run = False
+
+    def get_next_response(self):
+        return (self.default_response_status,
+                self.default_response_headers,
+                self.default_response_data)
+
+    @property
+    def requests(self):
+        """Use this property to get all requests that have been made
+        to the server
+        """
+        while True:
+            try:
+                self._requests.append(self.request_queue.get_nowait())
+            except queue.Empty:
+                break
+        return self._requests
+
+    @property
+    def full_address(self):
+        return "http://%s:%s" % self.address
+
+
+class PyPIRequestHandler(SimpleHTTPRequestHandler):
+    # we need to access the pypi server while serving the content
+    pypi_server = None
+
+    def serve_request(self):
+        """Serve the content.
+
+        Also record the requests to be accessed later. If trying to access an
+        url matching a static uri, serve static content, otherwise serve
+        what is provided by the `get_next_response` method.
+
+        If nothing is defined there, return a 404 header.
+        """
+        # record the request. Read the input only on PUT or POST requests
+        if self.command in ("PUT", "POST"):
+            if 'content-length' in self.headers:
+                request_data = self.rfile.read(
+                    int(self.headers['content-length']))
+            else:
+                request_data = self.rfile.read()
+
+        elif self.command in ("GET", "DELETE"):
+            request_data = ''
+
+        self.pypi_server.request_queue.put((self, request_data))
+
+        # serve the content from local disc if we request an URL beginning
+        # by a pattern defined in `static_paths`
+        url_parts = self.path.split("/")
+        if (len(url_parts) > 1 and
+                url_parts[1] in self.pypi_server.static_uri_paths):
+            data = None
+            # always take the last first.
+            fs_paths = []
+            fs_paths.extend(self.pypi_server.static_filesystem_paths)
+            fs_paths.reverse()
+            relative_path = self.path
+            for fs_path in fs_paths:
+                try:
+                    if self.path.endswith("/"):
+                        relative_path += "index.html"
+
+                    if relative_path.endswith('.tar.gz'):
+                        with open(fs_path + relative_path, 'br') as file:
+                            data = file.read()
+                        headers = [('Content-type', 'application/x-gtar')]
+                    else:
+                        with open(fs_path + relative_path) as file:
+                            data = file.read().encode()
+                        headers = [('Content-type', 'text/html')]
+
+                    self.make_response(data, headers=headers)
+
+                except IOError:
+                    pass
+
+            if data is None:
+                self.make_response("Not found", 404)
+
+        # otherwise serve the content from get_next_response
+        else:
+            # send back a response
+            status, headers, data = self.pypi_server.get_next_response()
+            self.make_response(data, status, headers)
+
+    do_POST = do_GET = do_DELETE = do_PUT = serve_request
+
+    def make_response(self, data, status=200,
+                      headers=[('Content-type', 'text/html')]):
+        """Send the response to the HTTP client"""
+        if not isinstance(status, int):
+            try:
+                status = int(status)
+            except ValueError:
+                # we probably got something like YYY Codename.
+                # Just get the first 3 digits
+                status = int(status[:3])
+
+        self.send_response(status)
+        for header, value in headers:
+            self.send_header(header, value)
+        self.end_headers()
+
+        if type(data) is str:
+            data = data.encode()
+
+        self.wfile.write(data)
+
+
+class PyPIXMLRPCServer(SimpleXMLRPCServer):
+    def server_bind(self):
+        """Override server_bind to store the server name."""
+        socketserver.TCPServer.server_bind(self)
+        host, port = self.socket.getsockname()[:2]
+        self.server_name = socket.getfqdn(host)
+        self.server_port = port
+
+
+class MockDist:
+    """Fake distribution, used in the Mock PyPI Server"""
+
+    def __init__(self, name, version="1.0", hidden=False, url="http://url/",
+             type="sdist", filename="", size=10000,
+             digest="123456", downloads=7, has_sig=False,
+             python_version="source", comment="comment",
+             author="John Doe", author_email="john@doe.name",
+             maintainer="Main Tayner", maintainer_email="maintainer_mail",
+             project_url="http://project_url/", homepage="http://homepage/",
+             keywords="", platform="UNKNOWN", classifiers=[], licence="",
+             description="Description", summary="Summary", stable_version="",
+             ordering="", documentation_id="", code_kwalitee_id="",
+             installability_id="", obsoletes=[], obsoletes_dist=[],
+             provides=[], provides_dist=[], requires=[], requires_dist=[],
+             requires_external=[], requires_python=""):
+
+        # basic fields
+        self.name = name
+        self.version = version
+        self.hidden = hidden
+
+        # URL infos
+        self.url = url
+        self.digest = digest
+        self.downloads = downloads
+        self.has_sig = has_sig
+        self.python_version = python_version
+        self.comment = comment
+        self.type = type
+
+        # metadata
+        self.author = author
+        self.author_email = author_email
+        self.maintainer = maintainer
+        self.maintainer_email = maintainer_email
+        self.project_url = project_url
+        self.homepage = homepage
+        self.keywords = keywords
+        self.platform = platform
+        self.classifiers = classifiers
+        self.licence = licence
+        self.description = description
+        self.summary = summary
+        self.stable_version = stable_version
+        self.ordering = ordering
+        self.cheesecake_documentation_id = documentation_id
+        self.cheesecake_code_kwalitee_id = code_kwalitee_id
+        self.cheesecake_installability_id = installability_id
+
+        self.obsoletes = obsoletes
+        self.obsoletes_dist = obsoletes_dist
+        self.provides = provides
+        self.provides_dist = provides_dist
+        self.requires = requires
+        self.requires_dist = requires_dist
+        self.requires_external = requires_external
+        self.requires_python = requires_python
+
+    def url_infos(self):
+        return {
+            'url': self.url,
+            'packagetype': self.type,
+            'filename': 'filename.tar.gz',
+            'size': '6000',
+            'md5_digest': self.digest,
+            'downloads': self.downloads,
+            'has_sig': self.has_sig,
+            'python_version': self.python_version,
+            'comment_text': self.comment,
+        }
+
+    def metadata(self):
+        return {
+            'maintainer': self.maintainer,
+            'project_url': [self.project_url],
+            'maintainer_email': self.maintainer_email,
+            'cheesecake_code_kwalitee_id': self.cheesecake_code_kwalitee_id,
+            'keywords': self.keywords,
+            'obsoletes_dist': self.obsoletes_dist,
+            'requires_external': self.requires_external,
+            'author': self.author,
+            'author_email': self.author_email,
+            'download_url': self.url,
+            'platform': self.platform,
+            'version': self.version,
+            'obsoletes': self.obsoletes,
+            'provides': self.provides,
+            'cheesecake_documentation_id': self.cheesecake_documentation_id,
+            '_pypi_hidden': self.hidden,
+            'description': self.description,
+            '_pypi_ordering': 19,
+            'requires_dist': self.requires_dist,
+            'requires_python': self.requires_python,
+            'classifiers': [],
+            'name': self.name,
+            'licence': self.licence,
+            'summary': self.summary,
+            'home_page': self.homepage,
+            'stable_version': self.stable_version,
+            'provides_dist': self.provides_dist or "%s (%s)" % (self.name,
+                                                              self.version),
+            'requires': self.requires,
+            'cheesecake_installability_id': self.cheesecake_installability_id,
+        }
+
+    def search_result(self):
+        return {
+            '_pypi_ordering': 0,
+            'version': self.version,
+            'name': self.name,
+            'summary': self.summary,
+        }
+
+
+class XMLRPCMockIndex:
+    """Mock XMLRPC server"""
+
+    def __init__(self, dists=[]):
+        self._dists = dists
+        self._search_result = []
+
+    def add_distributions(self, dists):
+        for dist in dists:
+            self._dists.append(MockDist(**dist))
+
+    def set_distributions(self, dists):
+        self._dists = []
+        self.add_distributions(dists)
+
+    def set_search_result(self, result):
+        """set a predefined search result"""
+        self._search_result = result
+
+    def _get_search_results(self):
+        results = []
+        for name in self._search_result:
+            found_dist = [d for d in self._dists if d.name == name]
+            if found_dist:
+                results.append(found_dist[0])
+            else:
+                dist = MockDist(name)
+                results.append(dist)
+                self._dists.append(dist)
+        return [r.search_result() for r in results]
+
+    def list_packages(self):
+        return [d.name for d in self._dists]
+
+    def package_releases(self, package_name, show_hidden=False):
+        if show_hidden:
+            # return all
+            return [d.version for d in self._dists if d.name == package_name]
+        else:
+            # return only un-hidden
+            return [d.version for d in self._dists if d.name == package_name
+                    and not d.hidden]
+
+    def release_urls(self, package_name, version):
+        return [d.url_infos() for d in self._dists
+                if d.name == package_name and d.version == version]
+
+    def release_data(self, package_name, version):
+        release = [d for d in self._dists
+                   if d.name == package_name and d.version == version]
+        if release:
+            return release[0].metadata()
+        else:
+            return {}
+
+    def search(self, spec, operator="and"):
+        return self._get_search_results()
diff --git a/Lib/packaging/tests/pypi_test_server.py b/Lib/packaging/tests/pypi_test_server.py
new file mode 100644
index 0000000..8c8c641
--- /dev/null
+++ b/Lib/packaging/tests/pypi_test_server.py
@@ -0,0 +1,59 @@
+"""Test PyPI Server implementation at testpypi.python.org, to use in tests.
+
+This is a drop-in replacement for the mock pypi server for testing against a
+real pypi server hosted by python.org especially for testing against.
+"""
+
+import unittest
+
+PYPI_DEFAULT_STATIC_PATH = None
+
+
+def use_xmlrpc_server(*server_args, **server_kwargs):
+    server_kwargs['serve_xmlrpc'] = True
+    return use_pypi_server(*server_args, **server_kwargs)
+
+
+def use_http_server(*server_args, **server_kwargs):
+    server_kwargs['serve_xmlrpc'] = False
+    return use_pypi_server(*server_args, **server_kwargs)
+
+
+def use_pypi_server(*server_args, **server_kwargs):
+    """Decorator to make use of the PyPIServer for test methods,
+    just when needed, and not for the entire duration of the testcase.
+    """
+    def wrapper(func):
+        def wrapped(*args, **kwargs):
+            server = PyPIServer(*server_args, **server_kwargs)
+            func(server=server, *args, **kwargs)
+        return wrapped
+    return wrapper
+
+
+class PyPIServerTestCase(unittest.TestCase):
+
+    def setUp(self):
+        super(PyPIServerTestCase, self).setUp()
+        self.pypi = PyPIServer()
+        self.pypi.start()
+        self.addCleanup(self.pypi.stop)
+
+
+class PyPIServer:
+    """Shim to access testpypi.python.org, for testing a real server."""
+
+    def __init__(self, test_static_path=None,
+                 static_filesystem_paths=["default"],
+                 static_uri_paths=["simple"], serve_xmlrpc=False):
+        self.address = ('testpypi.python.org', '80')
+
+    def start(self):
+        pass
+
+    def stop(self):
+        pass
+
+    @property
+    def full_address(self):
+        return "http://%s:%s" % self.address
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/packages/source/f/foobar/foobar-0.1.tar.gz b/Lib/packaging/tests/pypiserver/downloads_with_md5/packages/source/f/foobar/foobar-0.1.tar.gz
new file mode 100644
index 0000000..333961e
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/packages/source/f/foobar/foobar-0.1.tar.gz
Binary files differ
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html
new file mode 100644
index 0000000..b89f1bd
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html
@@ -0,0 +1,3 @@
+<html><body>
+<a href="badmd5-0.1.tar.gz#md5=3e3d86693d6564c807272b11b3069dfe" rel="download">badmd5-0.1.tar.gz</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/foobar/index.html
new file mode 100644
index 0000000..9e42b16
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/foobar/index.html
@@ -0,0 +1,3 @@
+<html><body>
+<a href="foobar-0.1.tar.gz#md5=fe18804c5b722ff024cabdf514924fc4" rel="download">foobar-0.1.tar.gz</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/index.html b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/index.html
new file mode 100644
index 0000000..9baee04
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/index.html
@@ -0,0 +1,2 @@
+<a href="foobar/">foobar/</a> 
+<a href="badmd5/">badmd5/</a> 
diff --git a/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/bar/index.html b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/bar/index.html
new file mode 100644
index 0000000..c3d42c5
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/bar/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for bar</title></head><body><h1>Links for bar</h1>
+<a rel="download" href="../../packages/source/F/bar/bar-1.0.tar.gz">bar-1.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/bar/bar-1.0.1.tar.gz">bar-1.0.1.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/bar/bar-2.0.tar.gz">bar-2.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/bar/bar-2.0.1.tar.gz">bar-2.0.1.tar.gz</a><br/> 
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/baz/index.html b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/baz/index.html
new file mode 100644
index 0000000..4f34312
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/baz/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for baz</title></head><body><h1>Links for baz</h1>
+<a rel="download" href="../../packages/source/F/baz/baz-1.0.tar.gz">baz-1.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/baz/baz-1.0.1.tar.gz">baz-1.0.1.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/baz/baz-2.0.tar.gz">baz-2.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/baz/baz-2.0.1.tar.gz">baz-2.0.1.tar.gz</a><br/> 
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/foo/index.html b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/foo/index.html
new file mode 100644
index 0000000..0565e11
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/foo/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for foo</title></head><body><h1>Links for foo</h1>
+<a rel="download" href="../../packages/source/F/foo/foo-1.0.tar.gz">foo-1.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/foo/foo-1.0.1.tar.gz">foo-1.0.1.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/foo/foo-2.0.tar.gz">foo-2.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/foo/foo-2.0.1.tar.gz">foo-2.0.1.tar.gz</a><br/> 
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/index.html b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/index.html
new file mode 100644
index 0000000..a70cfd3
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/index.html
@@ -0,0 +1,3 @@
+<a href="foo/">foo/</a> 
+<a href="bar/">bar/</a> 
+<a href="baz/">baz/</a> 
diff --git a/Lib/packaging/tests/pypiserver/project_list/simple/index.html b/Lib/packaging/tests/pypiserver/project_list/simple/index.html
new file mode 100644
index 0000000..b36d728
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/project_list/simple/index.html
@@ -0,0 +1,5 @@
+<a class="test" href="yeah">FooBar-bar</a>
+<a class="test" href="yeah">Foobar-baz</a>
+<a class="test" href="yeah">Baz-FooBar</a>
+<a class="test" href="yeah">Baz</a>
+<a class="test" href="yeah">Foo</a>
diff --git a/Lib/packaging/tests/pypiserver/test_found_links/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/test_found_links/simple/foobar/index.html
new file mode 100644
index 0000000..a282a4e
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/test_found_links/simple/foobar/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for Foobar</title></head><body><h1>Links for Foobar</h1>
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-1.0.tar.gz#md5=98fa833fdabcdd78d00245aead66c174">Foobar-1.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-1.0.1.tar.gz#md5=2351efb20f6b7b5d9ce80fa4cb1bd9ca">Foobar-1.0.1.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-2.0.tar.gz#md5=98fa833fdabcdd78d00245aead66c274">Foobar-2.0.tar.gz</a><br/> 
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-2.0.1.tar.gz#md5=2352efb20f6b7b5d9ce80fa4cb2bd9ca">Foobar-2.0.1.tar.gz</a><br/> 
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/test_found_links/simple/index.html b/Lib/packaging/tests/pypiserver/test_found_links/simple/index.html
new file mode 100644
index 0000000..a1a7bb7
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/test_found_links/simple/index.html
@@ -0,0 +1 @@
+<a href="foobar/">foobar/</a> 
diff --git a/Lib/packaging/tests/pypiserver/test_pypi_server/external/index.html b/Lib/packaging/tests/pypiserver/test_pypi_server/external/index.html
new file mode 100644
index 0000000..265ee0a
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/test_pypi_server/external/index.html
@@ -0,0 +1 @@
+index.html from external server
diff --git a/Lib/packaging/tests/pypiserver/test_pypi_server/simple/index.html b/Lib/packaging/tests/pypiserver/test_pypi_server/simple/index.html
new file mode 100644
index 0000000..6f97667
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/test_pypi_server/simple/index.html
@@ -0,0 +1 @@
+Yeah
diff --git a/Lib/packaging/tests/pypiserver/with_externals/external/external.html b/Lib/packaging/tests/pypiserver/with_externals/external/external.html
new file mode 100644
index 0000000..92e4702
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_externals/external/external.html
@@ -0,0 +1,3 @@
+<html><body>
+<a href="/foobar-0.1.tar.gz#md5=1__bad_md5___">bad old link</a>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/with_externals/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/with_externals/simple/foobar/index.html
new file mode 100644
index 0000000..b100a26
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_externals/simple/foobar/index.html
@@ -0,0 +1,4 @@
+<html><body>
+<a rel ="download" href="/foobar-0.1.tar.gz#md5=12345678901234567">foobar-0.1.tar.gz</a><br/>
+<a href="../../external/external.html" rel="homepage">external homepage</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/with_externals/simple/index.html b/Lib/packaging/tests/pypiserver/with_externals/simple/index.html
new file mode 100644
index 0000000..a1a7bb7
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_externals/simple/index.html
@@ -0,0 +1 @@
+<a href="foobar/">foobar/</a> 
diff --git a/Lib/packaging/tests/pypiserver/with_norel_links/external/homepage.html b/Lib/packaging/tests/pypiserver/with_norel_links/external/homepage.html
new file mode 100644
index 0000000..1cc0c32
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_norel_links/external/homepage.html
@@ -0,0 +1,7 @@
+<html>
+<body>
+<p>a rel=homepage HTML page</p>
+<a href="/foobar-2.0.tar.gz">foobar 2.0</a>
+</body>
+</html>
+
diff --git a/Lib/packaging/tests/pypiserver/with_norel_links/external/nonrel.html b/Lib/packaging/tests/pypiserver/with_norel_links/external/nonrel.html
new file mode 100644
index 0000000..f6ace22
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_norel_links/external/nonrel.html
@@ -0,0 +1 @@
+A page linked without rel="download" or rel="homepage" link.
diff --git a/Lib/packaging/tests/pypiserver/with_norel_links/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/with_norel_links/simple/foobar/index.html
new file mode 100644
index 0000000..171df93
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_norel_links/simple/foobar/index.html
@@ -0,0 +1,6 @@
+<html><body>
+<a rel="download" href="/foobar-0.1.tar.gz" rel="download">foobar-0.1.tar.gz</a><br/>
+<a href="../../external/homepage.html" rel="homepage">external homepage</a><br/>
+<a href="../../external/nonrel.html">unrelated link</a><br/>
+<a href="/unrelated-0.2.tar.gz">unrelated download</a></br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/with_norel_links/simple/index.html b/Lib/packaging/tests/pypiserver/with_norel_links/simple/index.html
new file mode 100644
index 0000000..a1a7bb7
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_norel_links/simple/index.html
@@ -0,0 +1 @@
+<a href="foobar/">foobar/</a> 
diff --git a/Lib/packaging/tests/pypiserver/with_real_externals/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/with_real_externals/simple/foobar/index.html
new file mode 100644
index 0000000..b2885ae
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_real_externals/simple/foobar/index.html
@@ -0,0 +1,4 @@
+<html><body>
+<a rel="download" href="/foobar-0.1.tar.gz#md5=0_correct_md5">foobar-0.1.tar.gz</a><br/>
+<a href="http://a-really-external-website/external/external.html" rel="homepage">external homepage</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/with_real_externals/simple/index.html b/Lib/packaging/tests/pypiserver/with_real_externals/simple/index.html
new file mode 100644
index 0000000..a1a7bb7
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_real_externals/simple/index.html
@@ -0,0 +1 @@
+<a href="foobar/">foobar/</a> 
diff --git a/Lib/packaging/tests/support.py b/Lib/packaging/tests/support.py
new file mode 100644
index 0000000..cf5d788
--- /dev/null
+++ b/Lib/packaging/tests/support.py
@@ -0,0 +1,259 @@
+"""Support code for packaging test cases.
+
+A few helper classes are provided: LoggingCatcher, TempdirManager and
+EnvironRestorer. They are written to be used as mixins::
+
+    from packaging.tests import unittest
+    from packaging.tests.support import LoggingCatcher
+
+    class SomeTestCase(LoggingCatcher, unittest.TestCase):
+
+If you need to define a setUp method on your test class, you have to
+call the mixin class' setUp method or it won't work (same thing for
+tearDown):
+
+        def setUp(self):
+            super(SomeTestCase, self).setUp()
+            ... # other setup code
+
+Also provided is a DummyCommand class, useful to mock commands in the
+tests of another command that needs them, a create_distribution function
+and a skip_unless_symlink decorator.
+
+Also provided is a DummyCommand class, useful to mock commands in the
+tests of another command that needs them, a create_distribution function
+and a skip_unless_symlink decorator.
+
+Each class or function has a docstring to explain its purpose and usage.
+"""
+
+import os
+import errno
+import shutil
+import logging
+import weakref
+import tempfile
+
+from packaging import logger
+from packaging.dist import Distribution
+from packaging.tests import unittest
+
+__all__ = ['LoggingCatcher', 'TempdirManager', 'EnvironRestorer',
+           'DummyCommand', 'unittest', 'create_distribution',
+           'skip_unless_symlink']
+
+
+class _TestHandler(logging.handlers.BufferingHandler):
+    # stolen and adapted from test.support
+
+    def __init__(self):
+        logging.handlers.BufferingHandler.__init__(self, 0)
+        self.setLevel(logging.DEBUG)
+
+    def shouldFlush(self):
+        return False
+
+    def emit(self, record):
+        self.buffer.append(record)
+
+
+class LoggingCatcher:
+    """TestCase-compatible mixin to receive logging calls.
+
+    Upon setUp, instances of this classes get a BufferingHandler that's
+    configured to record all messages logged to the 'packaging' logger.
+
+    Use get_logs to retrieve messages and self.loghandler.flush to discard
+    them.
+    """
+
+    def setUp(self):
+        super(LoggingCatcher, self).setUp()
+        self.loghandler = handler = _TestHandler()
+        logger.addHandler(handler)
+        self.addCleanup(logger.setLevel, logger.level)
+        logger.setLevel(logging.DEBUG)  # we want all messages
+
+    def tearDown(self):
+        handler = self.loghandler
+        # All this is necessary to properly shut down the logging system and
+        # avoid a regrtest complaint.  Thanks to Vinay Sajip for the help.
+        handler.close()
+        logger.removeHandler(handler)
+        for ref in weakref.getweakrefs(handler):
+            logging._removeHandlerRef(ref)
+        del self.loghandler
+        super(LoggingCatcher, self).tearDown()
+
+    def get_logs(self, *levels):
+        """Return all log messages with level in *levels*.
+
+        Without explicit levels given, returns all messages.
+        *levels* defaults to all levels.  For log calls with arguments (i.e.
+        logger.info('bla bla %s', arg)), the messages
+        Returns a list.
+
+        Example: self.get_logs(logging.WARN, logging.DEBUG).
+        """
+        if not levels:
+            return [log.getMessage() for log in self.loghandler.buffer]
+        return [log.getMessage() for log in self.loghandler.buffer
+                if log.levelno in levels]
+
+
+class TempdirManager:
+    """TestCase-compatible mixin to create temporary directories and files.
+
+    Directories and files created in a test_* method will be removed after it
+    has run.
+    """
+
+    def setUp(self):
+        super(TempdirManager, self).setUp()
+        self._basetempdir = tempfile.mkdtemp()
+
+    def tearDown(self):
+        shutil.rmtree(self._basetempdir, os.name in ('nt', 'cygwin'))
+        super(TempdirManager, self).tearDown()
+
+    def mktempfile(self):
+        """Create a read-write temporary file and return it."""
+
+        def _delete_file(filename):
+            try:
+                os.remove(filename)
+            except OSError as exc:
+                if exc.errno != errno.ENOENT:
+                    raise
+
+        fd, fn = tempfile.mkstemp(dir=self._basetempdir)
+        os.close(fd)
+        fp = open(fn, 'w+')
+        self.addCleanup(fp.close)
+        self.addCleanup(_delete_file, fn)
+        return fp
+
+    def mkdtemp(self):
+        """Create a temporary directory and return its path."""
+        d = tempfile.mkdtemp(dir=self._basetempdir)
+        return d
+
+    def write_file(self, path, content='xxx'):
+        """Write a file at the given path.
+
+        path can be a string, a tuple or a list; if it's a tuple or list,
+        os.path.join will be used to produce a path.
+        """
+        if isinstance(path, (list, tuple)):
+            path = os.path.join(*path)
+        f = open(path, 'w')
+        try:
+            f.write(content)
+        finally:
+            f.close()
+
+    def create_dist(self, **kw):
+        """Create a stub distribution object and files.
+
+        This function creates a Distribution instance (use keyword arguments
+        to customize it) and a temporary directory with a project structure
+        (currently an empty directory).
+
+        It returns the path to the directory and the Distribution instance.
+        You can use self.write_file to write any file in that
+        directory, e.g. setup scripts or Python modules.
+        """
+        if 'name' not in kw:
+            kw['name'] = 'foo'
+        tmp_dir = self.mkdtemp()
+        project_dir = os.path.join(tmp_dir, kw['name'])
+        os.mkdir(project_dir)
+        dist = Distribution(attrs=kw)
+        return project_dir, dist
+
+    def assertIsFile(self, *args):
+        path = os.path.join(*args)
+        dirname = os.path.dirname(path)
+        file = os.path.basename(path)
+        if os.path.isdir(dirname):
+            files = os.listdir(dirname)
+            msg = "%s not found in %s: %s" % (file, dirname, files)
+            assert os.path.isfile(path), msg
+        else:
+            raise AssertionError(
+                    '%s not found. %s does not exist' % (file, dirname))
+
+    def assertIsNotFile(self, *args):
+        path = os.path.join(*args)
+        self.assertFalse(os.path.isfile(path), "%r exists" % path)
+
+
+class EnvironRestorer:
+    """TestCase-compatible mixin to restore or delete environment variables.
+
+    The variables to restore (or delete if they were not originally present)
+    must be explicitly listed in self.restore_environ.  It's better to be
+    aware of what we're modifying instead of saving and restoring the whole
+    environment.
+    """
+
+    def setUp(self):
+        super(EnvironRestorer, self).setUp()
+        self._saved = []
+        self._added = []
+        for key in self.restore_environ:
+            if key in os.environ:
+                self._saved.append((key, os.environ[key]))
+            else:
+                self._added.append(key)
+
+    def tearDown(self):
+        for key, value in self._saved:
+            os.environ[key] = value
+        for key in self._added:
+            os.environ.pop(key, None)
+        super(EnvironRestorer, self).tearDown()
+
+
+class DummyCommand:
+    """Class to store options for retrieval via set_undefined_options().
+
+    Useful for mocking one dependency command in the tests for another
+    command, see e.g. the dummy build command in test_build_scripts.
+    """
+
+    def __init__(self, **kwargs):
+        for kw, val in kwargs.items():
+            setattr(self, kw, val)
+
+    def ensure_finalized(self):
+        pass
+
+
+class TestDistribution(Distribution):
+    """Distribution subclasses that avoids the default search for
+    configuration files.
+
+    The ._config_files attribute must be set before
+    .parse_config_files() is called.
+    """
+
+    def find_config_files(self):
+        return self._config_files
+
+
+def create_distribution(configfiles=()):
+    """Prepares a distribution with given config files parsed."""
+    d = TestDistribution()
+    d.config.find_config_files = d.find_config_files
+    d._config_files = configfiles
+    d.parse_config_files()
+    d.parse_command_line()
+    return d
+
+
+try:
+    from test.support import skip_unless_symlink
+except ImportError:
+    skip_unless_symlink = unittest.skip(
+        'requires test.support.skip_unless_symlink')
diff --git a/Lib/packaging/tests/test_ccompiler.py b/Lib/packaging/tests/test_ccompiler.py
new file mode 100644
index 0000000..dd4bdd9
--- /dev/null
+++ b/Lib/packaging/tests/test_ccompiler.py
@@ -0,0 +1,15 @@
+"""Tests for distutils.compiler.ccompiler."""
+
+from packaging.compiler import ccompiler
+from packaging.tests import unittest, support
+
+
+class CCompilerTestCase(unittest.TestCase):
+    pass  # XXX need some tests on CCompiler
+
+
+def test_suite():
+    return unittest.makeSuite(CCompilerTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_bdist.py b/Lib/packaging/tests/test_command_bdist.py
new file mode 100644
index 0000000..1522b7e
--- /dev/null
+++ b/Lib/packaging/tests/test_command_bdist.py
@@ -0,0 +1,77 @@
+"""Tests for distutils.command.bdist."""
+
+from packaging import util
+from packaging.command.bdist import bdist, show_formats
+
+from packaging.tests import unittest, support, captured_stdout
+
+
+class BuildTestCase(support.TempdirManager,
+                    support.LoggingCatcher,
+                    unittest.TestCase):
+
+    def _mock_get_platform(self):
+        self._get_platform_called = True
+        return self._get_platform()
+
+    def setUp(self):
+        super(BuildTestCase, self).setUp()
+
+        # mock util.get_platform
+        self._get_platform_called = False
+        self._get_platform = util.get_platform
+        util.get_platform = self._mock_get_platform
+
+    def tearDown(self):
+        super(BuildTestCase, self).tearDown()
+        util.get_platform = self._get_platform
+
+    def test_formats(self):
+
+        # let's create a command and make sure
+        # we can fix the format
+        pkg_pth, dist = self.create_dist()
+        cmd = bdist(dist)
+        cmd.formats = ['msi']
+        cmd.ensure_finalized()
+        self.assertEqual(cmd.formats, ['msi'])
+
+        # what format bdist offers ?
+        # XXX an explicit list in bdist is
+        # not the best way to  bdist_* commands
+        # we should add a registry
+        formats = sorted(('zip', 'gztar', 'bztar', 'ztar',
+                          'tar', 'wininst', 'msi'))
+        found = sorted(cmd.format_command)
+        self.assertEqual(found, formats)
+
+    def test_skip_build(self):
+        pkg_pth, dist = self.create_dist()
+        cmd = bdist(dist)
+        cmd.skip_build = False
+        cmd.formats = ['ztar']
+        cmd.ensure_finalized()
+        self.assertFalse(self._get_platform_called)
+
+        pkg_pth, dist = self.create_dist()
+        cmd = bdist(dist)
+        cmd.skip_build = True
+        cmd.formats = ['ztar']
+        cmd.ensure_finalized()
+        self.assertTrue(self._get_platform_called)
+
+    def test_show_formats(self):
+        __, stdout = captured_stdout(show_formats)
+
+        # the output should be a header line + one line per format
+        num_formats = len(bdist.format_commands)
+        output = [line for line in stdout.split('\n')
+                  if line.strip().startswith('--formats=')]
+        self.assertEqual(len(output), num_formats)
+
+
+def test_suite():
+    return unittest.makeSuite(BuildTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_bdist_dumb.py b/Lib/packaging/tests/test_command_bdist_dumb.py
new file mode 100644
index 0000000..ce1563f
--- /dev/null
+++ b/Lib/packaging/tests/test_command_bdist_dumb.py
@@ -0,0 +1,103 @@
+"""Tests for distutils.command.bdist_dumb."""
+
+import sys
+import os
+
+# zlib is not used here, but if it's not available
+# test_simple_built will fail
+try:
+    import zlib
+except ImportError:
+    zlib = None
+
+from packaging.dist import Distribution
+from packaging.command.bdist_dumb import bdist_dumb
+from packaging.tests import unittest, support
+
+
+SETUP_PY = """\
+from distutils.run import setup
+import foo
+
+setup(name='foo', version='0.1', py_modules=['foo'],
+      url='xxx', author='xxx', author_email='xxx')
+"""
+
+
+class BuildDumbTestCase(support.TempdirManager,
+                        support.LoggingCatcher,
+                        unittest.TestCase):
+
+    def setUp(self):
+        super(BuildDumbTestCase, self).setUp()
+        self.old_location = os.getcwd()
+        self.old_sys_argv = sys.argv, sys.argv[:]
+
+    def tearDown(self):
+        os.chdir(self.old_location)
+        sys.argv = self.old_sys_argv[0]
+        sys.argv[:] = self.old_sys_argv[1]
+        super(BuildDumbTestCase, self).tearDown()
+
+    @unittest.skipUnless(zlib, "requires zlib")
+    def test_simple_built(self):
+
+        # let's create a simple package
+        tmp_dir = self.mkdtemp()
+        pkg_dir = os.path.join(tmp_dir, 'foo')
+        os.mkdir(pkg_dir)
+        self.write_file((pkg_dir, 'setup.py'), SETUP_PY)
+        self.write_file((pkg_dir, 'foo.py'), '#')
+        self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py')
+        self.write_file((pkg_dir, 'README'), '')
+
+        dist = Distribution({'name': 'foo', 'version': '0.1',
+                             'py_modules': ['foo'],
+                             'url': 'xxx', 'author': 'xxx',
+                             'author_email': 'xxx'})
+        dist.script_name = 'setup.py'
+        os.chdir(pkg_dir)
+
+        sys.argv[:] = ['setup.py']
+        cmd = bdist_dumb(dist)
+
+        # so the output is the same no matter
+        # what is the platform
+        cmd.format = 'zip'
+
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # see what we have
+        dist_created = os.listdir(os.path.join(pkg_dir, 'dist'))
+        base = "%s.%s" % (dist.get_fullname(), cmd.plat_name)
+        if os.name == 'os2':
+            base = base.replace(':', '-')
+
+        wanted = ['%s.zip' % base]
+        self.assertEqual(dist_created, wanted)
+
+        # now let's check what we have in the zip file
+        # XXX to be done
+
+    def test_finalize_options(self):
+        pkg_dir, dist = self.create_dist()
+        os.chdir(pkg_dir)
+        cmd = bdist_dumb(dist)
+        self.assertEqual(cmd.bdist_dir, None)
+        cmd.finalize_options()
+
+        # bdist_dir is initialized to bdist_base/dumb if not set
+        base = cmd.get_finalized_command('bdist').bdist_base
+        self.assertEqual(cmd.bdist_dir, os.path.join(base, 'dumb'))
+
+        # the format is set to a default value depending on the os.name
+        default = cmd.default_format[os.name]
+        self.assertEqual(cmd.format, default)
+
+
+def test_suite():
+    return unittest.makeSuite(BuildDumbTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_bdist_msi.py b/Lib/packaging/tests/test_command_bdist_msi.py
new file mode 100644
index 0000000..fded962
--- /dev/null
+++ b/Lib/packaging/tests/test_command_bdist_msi.py
@@ -0,0 +1,25 @@
+"""Tests for distutils.command.bdist_msi."""
+import sys
+
+from packaging.tests import unittest, support
+
+
+class BDistMSITestCase(support.TempdirManager,
+                       support.LoggingCatcher,
+                       unittest.TestCase):
+
+    @unittest.skipUnless(sys.platform == "win32", "runs only on win32")
+    def test_minimal(self):
+        # minimal test XXX need more tests
+        from packaging.command.bdist_msi import bdist_msi
+        pkg_pth, dist = self.create_dist()
+        cmd = bdist_msi(dist)
+        cmd.ensure_finalized()
+
+
+def test_suite():
+    return unittest.makeSuite(BDistMSITestCase)
+
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_bdist_wininst.py b/Lib/packaging/tests/test_command_bdist_wininst.py
new file mode 100644
index 0000000..09bdaad
--- /dev/null
+++ b/Lib/packaging/tests/test_command_bdist_wininst.py
@@ -0,0 +1,32 @@
+"""Tests for distutils.command.bdist_wininst."""
+
+from packaging.command.bdist_wininst import bdist_wininst
+from packaging.tests import unittest, support
+
+
+class BuildWinInstTestCase(support.TempdirManager,
+                           support.LoggingCatcher,
+                           unittest.TestCase):
+
+    def test_get_exe_bytes(self):
+
+        # issue5731: command was broken on non-windows platforms
+        # this test makes sure it works now for every platform
+        # let's create a command
+        pkg_pth, dist = self.create_dist()
+        cmd = bdist_wininst(dist)
+        cmd.ensure_finalized()
+
+        # let's run the code that finds the right wininst*.exe file
+        # and make sure it finds it and returns its content
+        # no matter what platform we have
+        exe_file = cmd.get_exe_bytes()
+        self.assertGreater(len(exe_file), 10)
+
+
+def test_suite():
+    return unittest.makeSuite(BuildWinInstTestCase)
+
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_build.py b/Lib/packaging/tests/test_command_build.py
new file mode 100644
index 0000000..91fbe42
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build.py
@@ -0,0 +1,55 @@
+"""Tests for distutils.command.build."""
+import os
+import sys
+
+from packaging.command.build import build
+from sysconfig import get_platform
+from packaging.tests import unittest, support
+
+
+class BuildTestCase(support.TempdirManager,
+                    support.LoggingCatcher,
+                    unittest.TestCase):
+
+    def test_finalize_options(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = build(dist)
+        cmd.finalize_options()
+
+        # if not specified, plat_name gets the current platform
+        self.assertEqual(cmd.plat_name, get_platform())
+
+        # build_purelib is build + lib
+        wanted = os.path.join(cmd.build_base, 'lib')
+        self.assertEqual(cmd.build_purelib, wanted)
+
+        # build_platlib is 'build/lib.platform-x.x[-pydebug]'
+        # examples:
+        #   build/lib.macosx-10.3-i386-2.7
+        plat_spec = '.%s-%s' % (cmd.plat_name, sys.version[0:3])
+        if hasattr(sys, 'gettotalrefcount'):
+            self.assertTrue(cmd.build_platlib.endswith('-pydebug'))
+            plat_spec += '-pydebug'
+        wanted = os.path.join(cmd.build_base, 'lib' + plat_spec)
+        self.assertEqual(cmd.build_platlib, wanted)
+
+        # by default, build_lib = build_purelib
+        self.assertEqual(cmd.build_lib, cmd.build_purelib)
+
+        # build_temp is build/temp.<plat>
+        wanted = os.path.join(cmd.build_base, 'temp' + plat_spec)
+        self.assertEqual(cmd.build_temp, wanted)
+
+        # build_scripts is build/scripts-x.x
+        wanted = os.path.join(cmd.build_base, 'scripts-' + sys.version[0:3])
+        self.assertEqual(cmd.build_scripts, wanted)
+
+        # executable is os.path.normpath(sys.executable)
+        self.assertEqual(cmd.executable, os.path.normpath(sys.executable))
+
+
+def test_suite():
+    return unittest.makeSuite(BuildTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_build_clib.py b/Lib/packaging/tests/test_command_build_clib.py
new file mode 100644
index 0000000..a2a8583
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build_clib.py
@@ -0,0 +1,141 @@
+"""Tests for distutils.command.build_clib."""
+import os
+import sys
+
+from packaging.util import find_executable
+from packaging.command.build_clib import build_clib
+from packaging.errors import PackagingSetupError
+from packaging.tests import unittest, support
+
+
+class BuildCLibTestCase(support.TempdirManager,
+                        support.LoggingCatcher,
+                        unittest.TestCase):
+
+    def test_check_library_dist(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = build_clib(dist)
+
+        # 'libraries' option must be a list
+        self.assertRaises(PackagingSetupError, cmd.check_library_list, 'foo')
+
+        # each element of 'libraries' must a 2-tuple
+        self.assertRaises(PackagingSetupError, cmd.check_library_list,
+                          ['foo1', 'foo2'])
+
+        # first element of each tuple in 'libraries'
+        # must be a string (the library name)
+        self.assertRaises(PackagingSetupError, cmd.check_library_list,
+                          [(1, 'foo1'), ('name', 'foo2')])
+
+        # library name may not contain directory separators
+        self.assertRaises(PackagingSetupError, cmd.check_library_list,
+                          [('name', 'foo1'),
+                           ('another/name', 'foo2')])
+
+        # second element of each tuple must be a dictionary (build info)
+        self.assertRaises(PackagingSetupError, cmd.check_library_list,
+                          [('name', {}),
+                           ('another', 'foo2')])
+
+        # those work
+        libs = [('name', {}), ('name', {'ok': 'good'})]
+        cmd.check_library_list(libs)
+
+    def test_get_source_files(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = build_clib(dist)
+
+        # "in 'libraries' option 'sources' must be present and must be
+        # a list of source filenames
+        cmd.libraries = [('name', {})]
+        self.assertRaises(PackagingSetupError, cmd.get_source_files)
+
+        cmd.libraries = [('name', {'sources': 1})]
+        self.assertRaises(PackagingSetupError, cmd.get_source_files)
+
+        cmd.libraries = [('name', {'sources': ['a', 'b']})]
+        self.assertEqual(cmd.get_source_files(), ['a', 'b'])
+
+        cmd.libraries = [('name', {'sources': ('a', 'b')})]
+        self.assertEqual(cmd.get_source_files(), ['a', 'b'])
+
+        cmd.libraries = [('name', {'sources': ('a', 'b')}),
+                         ('name2', {'sources': ['c', 'd']})]
+        self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd'])
+
+    def test_build_libraries(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = build_clib(dist)
+
+        class FakeCompiler:
+            def compile(*args, **kw):
+                pass
+            create_static_lib = compile
+
+        cmd.compiler = FakeCompiler()
+
+        # build_libraries is also doing a bit of type checking
+        lib = [('name', {'sources': 'notvalid'})]
+        self.assertRaises(PackagingSetupError, cmd.build_libraries, lib)
+
+        lib = [('name', {'sources': []})]
+        cmd.build_libraries(lib)
+
+        lib = [('name', {'sources': ()})]
+        cmd.build_libraries(lib)
+
+    def test_finalize_options(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = build_clib(dist)
+
+        cmd.include_dirs = 'one-dir'
+        cmd.finalize_options()
+        self.assertEqual(cmd.include_dirs, ['one-dir'])
+
+        cmd.include_dirs = None
+        cmd.finalize_options()
+        self.assertEqual(cmd.include_dirs, [])
+
+        cmd.distribution.libraries = 'WONTWORK'
+        self.assertRaises(PackagingSetupError, cmd.finalize_options)
+
+    @unittest.skipIf(sys.platform == 'win32', 'disabled on win32')
+    def test_run(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = build_clib(dist)
+
+        foo_c = os.path.join(pkg_dir, 'foo.c')
+        self.write_file(foo_c, 'int main(void) { return 1;}\n')
+        cmd.libraries = [('foo', {'sources': [foo_c]})]
+
+        build_temp = os.path.join(pkg_dir, 'build')
+        os.mkdir(build_temp)
+        cmd.build_temp = build_temp
+        cmd.build_clib = build_temp
+
+        # before we run the command, we want to make sure
+        # all commands are present on the system
+        # by creating a compiler and checking its executables
+        from packaging.compiler import new_compiler, customize_compiler
+
+        compiler = new_compiler()
+        customize_compiler(compiler)
+        for ccmd in compiler.executables.values():
+            if ccmd is None:
+                continue
+            if find_executable(ccmd[0]) is None:
+                raise unittest.SkipTest("can't test")
+
+        # this should work
+        cmd.run()
+
+        # let's check the result
+        self.assertIn('libfoo.a', os.listdir(build_temp))
+
+
+def test_suite():
+    return unittest.makeSuite(BuildCLibTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_build_ext.py b/Lib/packaging/tests/test_command_build_ext.py
new file mode 100644
index 0000000..2d79842
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build_ext.py
@@ -0,0 +1,353 @@
+import os
+import sys
+import site
+import shutil
+import sysconfig
+from io import StringIO
+from packaging.dist import Distribution
+from packaging.errors import UnknownFileError, CompileError
+from packaging.command.build_ext import build_ext
+from packaging.compiler.extension import Extension
+
+from packaging.tests import support, unittest, verbose, unload
+
+# http://bugs.python.org/issue4373
+# Don't load the xx module more than once.
+ALREADY_TESTED = False
+
+
+def _get_source_filename():
+    srcdir = sysconfig.get_config_var('srcdir')
+    return os.path.join(srcdir, 'Modules', 'xxmodule.c')
+
+
+class BuildExtTestCase(support.TempdirManager,
+                       support.LoggingCatcher,
+                       unittest.TestCase):
+    def setUp(self):
+        # Create a simple test environment
+        # Note that we're making changes to sys.path
+        super(BuildExtTestCase, self).setUp()
+        self.tmp_dir = self.mkdtemp()
+        self.sys_path = sys.path, sys.path[:]
+        sys.path.append(self.tmp_dir)
+        shutil.copy(_get_source_filename(), self.tmp_dir)
+        self.old_user_base = site.USER_BASE
+        site.USER_BASE = self.mkdtemp()
+        build_ext.USER_BASE = site.USER_BASE
+
+    def test_build_ext(self):
+        global ALREADY_TESTED
+        xx_c = os.path.join(self.tmp_dir, 'xxmodule.c')
+        xx_ext = Extension('xx', [xx_c])
+        dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]})
+        dist.package_dir = self.tmp_dir
+        cmd = build_ext(dist)
+        if os.name == "nt":
+            # On Windows, we must build a debug version iff running
+            # a debug build of Python
+            cmd.debug = sys.executable.endswith("_d.exe")
+        cmd.build_lib = self.tmp_dir
+        cmd.build_temp = self.tmp_dir
+
+        old_stdout = sys.stdout
+        if not verbose:
+            # silence compiler output
+            sys.stdout = StringIO()
+        try:
+            cmd.ensure_finalized()
+            cmd.run()
+        finally:
+            sys.stdout = old_stdout
+
+        if ALREADY_TESTED:
+            return
+        else:
+            ALREADY_TESTED = True
+
+        import xx
+
+        for attr in ('error', 'foo', 'new', 'roj'):
+            self.assertTrue(hasattr(xx, attr))
+
+        self.assertEqual(xx.foo(2, 5), 7)
+        self.assertEqual(xx.foo(13, 15), 28)
+        self.assertEqual(xx.new().demo(), None)
+        doc = 'This is a template module just for instruction.'
+        self.assertEqual(xx.__doc__, doc)
+        self.assertTrue(isinstance(xx.Null(), xx.Null))
+        self.assertTrue(isinstance(xx.Str(), xx.Str))
+
+    def tearDown(self):
+        # Get everything back to normal
+        unload('xx')
+        sys.path = self.sys_path[0]
+        sys.path[:] = self.sys_path[1]
+        if sys.version > "2.6":
+            site.USER_BASE = self.old_user_base
+            build_ext.USER_BASE = self.old_user_base
+
+        super(BuildExtTestCase, self).tearDown()
+
+    def test_solaris_enable_shared(self):
+        dist = Distribution({'name': 'xx'})
+        cmd = build_ext(dist)
+        old = sys.platform
+
+        sys.platform = 'sunos'  # fooling finalize_options
+        from sysconfig import _CONFIG_VARS
+
+        old_var = _CONFIG_VARS.get('Py_ENABLE_SHARED')
+        _CONFIG_VARS['Py_ENABLE_SHARED'] = 1
+        try:
+            cmd.ensure_finalized()
+        finally:
+            sys.platform = old
+            if old_var is None:
+                del _CONFIG_VARS['Py_ENABLE_SHARED']
+            else:
+                _CONFIG_VARS['Py_ENABLE_SHARED'] = old_var
+
+        # make sure we get some library dirs under solaris
+        self.assertGreater(len(cmd.library_dirs), 0)
+
+    @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+    def test_user_site(self):
+        dist = Distribution({'name': 'xx'})
+        cmd = build_ext(dist)
+
+        # making sure the user option is there
+        options = [name for name, short, label in
+                   cmd.user_options]
+        self.assertIn('user', options)
+
+        # setting a value
+        cmd.user = True
+
+        # setting user based lib and include
+        lib = os.path.join(site.USER_BASE, 'lib')
+        incl = os.path.join(site.USER_BASE, 'include')
+        os.mkdir(lib)
+        os.mkdir(incl)
+
+        # let's run finalize
+        cmd.ensure_finalized()
+
+        # see if include_dirs and library_dirs
+        # were set
+        self.assertIn(lib, cmd.library_dirs)
+        self.assertIn(lib, cmd.rpath)
+        self.assertIn(incl, cmd.include_dirs)
+
+    def test_optional_extension(self):
+
+        # this extension will fail, but let's ignore this failure
+        # with the optional argument.
+        modules = [Extension('foo', ['xxx'], optional=False)]
+        dist = Distribution({'name': 'xx', 'ext_modules': modules})
+        cmd = build_ext(dist)
+        cmd.ensure_finalized()
+        self.assertRaises((UnknownFileError, CompileError),
+                          cmd.run)  # should raise an error
+
+        modules = [Extension('foo', ['xxx'], optional=True)]
+        dist = Distribution({'name': 'xx', 'ext_modules': modules})
+        cmd = build_ext(dist)
+        cmd.ensure_finalized()
+        cmd.run()  # should pass
+
+    def test_finalize_options(self):
+        # Make sure Python's include directories (for Python.h, pyconfig.h,
+        # etc.) are in the include search path.
+        modules = [Extension('foo', ['xxx'], optional=False)]
+        dist = Distribution({'name': 'xx', 'ext_modules': modules})
+        cmd = build_ext(dist)
+        cmd.finalize_options()
+
+        py_include = sysconfig.get_path('include')
+        self.assertIn(py_include, cmd.include_dirs)
+
+        plat_py_include = sysconfig.get_path('platinclude')
+        self.assertIn(plat_py_include, cmd.include_dirs)
+
+        # make sure cmd.libraries is turned into a list
+        # if it's a string
+        cmd = build_ext(dist)
+        cmd.libraries = 'my_lib'
+        cmd.finalize_options()
+        self.assertEqual(cmd.libraries, ['my_lib'])
+
+        # make sure cmd.library_dirs is turned into a list
+        # if it's a string
+        cmd = build_ext(dist)
+        cmd.library_dirs = 'my_lib_dir'
+        cmd.finalize_options()
+        self.assertIn('my_lib_dir', cmd.library_dirs)
+
+        # make sure rpath is turned into a list
+        # if it's a list of os.pathsep's paths
+        cmd = build_ext(dist)
+        cmd.rpath = os.pathsep.join(['one', 'two'])
+        cmd.finalize_options()
+        self.assertEqual(cmd.rpath, ['one', 'two'])
+
+        # XXX more tests to perform for win32
+
+        # make sure define is turned into 2-tuples
+        # strings if they are ','-separated strings
+        cmd = build_ext(dist)
+        cmd.define = 'one,two'
+        cmd.finalize_options()
+        self.assertEqual(cmd.define, [('one', '1'), ('two', '1')])
+
+        # make sure undef is turned into a list of
+        # strings if they are ','-separated strings
+        cmd = build_ext(dist)
+        cmd.undef = 'one,two'
+        cmd.finalize_options()
+        self.assertEqual(cmd.undef, ['one', 'two'])
+
+        # make sure swig_opts is turned into a list
+        cmd = build_ext(dist)
+        cmd.swig_opts = None
+        cmd.finalize_options()
+        self.assertEqual(cmd.swig_opts, [])
+
+        cmd = build_ext(dist)
+        cmd.swig_opts = '1 2'
+        cmd.finalize_options()
+        self.assertEqual(cmd.swig_opts, ['1', '2'])
+
+    def test_get_source_files(self):
+        modules = [Extension('foo', ['xxx'], optional=False)]
+        dist = Distribution({'name': 'xx', 'ext_modules': modules})
+        cmd = build_ext(dist)
+        cmd.ensure_finalized()
+        self.assertEqual(cmd.get_source_files(), ['xxx'])
+
+    def test_compiler_option(self):
+        # cmd.compiler is an option and
+        # should not be overriden by a compiler instance
+        # when the command is run
+        dist = Distribution()
+        cmd = build_ext(dist)
+        cmd.compiler = 'unix'
+        cmd.ensure_finalized()
+        cmd.run()
+        self.assertEqual(cmd.compiler, 'unix')
+
+    def test_get_outputs(self):
+        tmp_dir = self.mkdtemp()
+        c_file = os.path.join(tmp_dir, 'foo.c')
+        self.write_file(c_file, 'void initfoo(void) {};\n')
+        ext = Extension('foo', [c_file], optional=False)
+        dist = Distribution({'name': 'xx',
+                             'ext_modules': [ext]})
+        cmd = build_ext(dist)
+        cmd.ensure_finalized()
+        self.assertEqual(len(cmd.get_outputs()), 1)
+
+        if os.name == "nt":
+            cmd.debug = sys.executable.endswith("_d.exe")
+
+        cmd.build_lib = os.path.join(self.tmp_dir, 'build')
+        cmd.build_temp = os.path.join(self.tmp_dir, 'tempt')
+
+        # issue #5977 : distutils build_ext.get_outputs
+        # returns wrong result with --inplace
+        other_tmp_dir = os.path.realpath(self.mkdtemp())
+        old_wd = os.getcwd()
+        os.chdir(other_tmp_dir)
+        try:
+            cmd.inplace = True
+            cmd.run()
+            so_file = cmd.get_outputs()[0]
+        finally:
+            os.chdir(old_wd)
+        self.assertTrue(os.path.exists(so_file))
+        so_ext = sysconfig.get_config_var('SO')
+        self.assertTrue(so_file.endswith(so_ext))
+        so_dir = os.path.dirname(so_file)
+        self.assertEqual(so_dir, other_tmp_dir)
+
+        cmd.inplace = False
+        cmd.run()
+        so_file = cmd.get_outputs()[0]
+        self.assertTrue(os.path.exists(so_file))
+        self.assertTrue(so_file.endswith(so_ext))
+        so_dir = os.path.dirname(so_file)
+        self.assertEqual(so_dir, cmd.build_lib)
+
+        # inplace = False, cmd.package = 'bar'
+        build_py = cmd.get_finalized_command('build_py')
+        build_py.package_dir = 'bar'
+        path = cmd.get_ext_fullpath('foo')
+        # checking that the last directory is the build_dir
+        path = os.path.split(path)[0]
+        self.assertEqual(path, cmd.build_lib)
+
+        # inplace = True, cmd.package = 'bar'
+        cmd.inplace = True
+        other_tmp_dir = os.path.realpath(self.mkdtemp())
+        old_wd = os.getcwd()
+        os.chdir(other_tmp_dir)
+        try:
+            path = cmd.get_ext_fullpath('foo')
+        finally:
+            os.chdir(old_wd)
+        # checking that the last directory is bar
+        path = os.path.split(path)[0]
+        lastdir = os.path.split(path)[-1]
+        self.assertEqual(lastdir, 'bar')
+
+    def test_ext_fullpath(self):
+        ext = sysconfig.get_config_vars()['SO']
+        # building lxml.etree inplace
+        #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c')
+        #etree_ext = Extension('lxml.etree', [etree_c])
+        #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]})
+        dist = Distribution()
+        cmd = build_ext(dist)
+        cmd.inplace = True
+        cmd.distribution.package_dir = 'src'
+        cmd.distribution.packages = ['lxml', 'lxml.html']
+        curdir = os.getcwd()
+        wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
+        path = cmd.get_ext_fullpath('lxml.etree')
+        self.assertEqual(wanted, path)
+
+        # building lxml.etree not inplace
+        cmd.inplace = False
+        cmd.build_lib = os.path.join(curdir, 'tmpdir')
+        wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext)
+        path = cmd.get_ext_fullpath('lxml.etree')
+        self.assertEqual(wanted, path)
+
+        # building twisted.runner.portmap not inplace
+        build_py = cmd.get_finalized_command('build_py')
+        build_py.package_dir = None
+        cmd.distribution.packages = ['twisted', 'twisted.runner.portmap']
+        path = cmd.get_ext_fullpath('twisted.runner.portmap')
+        wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner',
+                              'portmap' + ext)
+        self.assertEqual(wanted, path)
+
+        # building twisted.runner.portmap inplace
+        cmd.inplace = True
+        path = cmd.get_ext_fullpath('twisted.runner.portmap')
+        wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext)
+        self.assertEqual(wanted, path)
+
+
+def test_suite():
+    src = _get_source_filename()
+    if not os.path.exists(src):
+        if verbose:
+            print ('test_build_ext: Cannot find source code (test'
+                   ' must run in python build dir)')
+        return unittest.TestSuite()
+    else:
+        return unittest.makeSuite(BuildExtTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_build_py.py b/Lib/packaging/tests/test_command_build_py.py
new file mode 100644
index 0000000..9b40e6d
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build_py.py
@@ -0,0 +1,124 @@
+"""Tests for distutils.command.build_py."""
+
+import os
+import sys
+
+from packaging.command.build_py import build_py
+from packaging.dist import Distribution
+from packaging.errors import PackagingFileError
+
+from packaging.tests import unittest, support
+
+
+class BuildPyTestCase(support.TempdirManager,
+                      support.LoggingCatcher,
+                      unittest.TestCase):
+
+    def test_package_data(self):
+        sources = self.mkdtemp()
+        pkg_dir = os.path.join(sources, 'pkg')
+        os.mkdir(pkg_dir)
+        f = open(os.path.join(pkg_dir, "__init__.py"), "w")
+        try:
+            f.write("# Pretend this is a package.")
+        finally:
+            f.close()
+        f = open(os.path.join(pkg_dir, "README.txt"), "w")
+        try:
+            f.write("Info about this package")
+        finally:
+            f.close()
+
+        destination = self.mkdtemp()
+
+        dist = Distribution({"packages": ["pkg"],
+                             "package_dir": sources})
+        # script_name need not exist, it just need to be initialized
+
+        dist.script_name = os.path.join(sources, "setup.py")
+        dist.command_obj["build"] = support.DummyCommand(
+            force=False,
+            build_lib=destination,
+            use_2to3_fixers=None,
+            convert_2to3_doctests=None,
+            use_2to3=False)
+        dist.packages = ["pkg"]
+        dist.package_data = {"pkg": ["README.txt"]}
+        dist.package_dir = sources
+
+        cmd = build_py(dist)
+        cmd.compile = True
+        cmd.ensure_finalized()
+        self.assertEqual(cmd.package_data, dist.package_data)
+
+        cmd.run()
+
+        # This makes sure the list of outputs includes byte-compiled
+        # files for Python modules but not for package data files
+        # (there shouldn't *be* byte-code files for those!).
+        #
+        self.assertEqual(len(cmd.get_outputs()), 3)
+        pkgdest = os.path.join(destination, "pkg")
+        files = os.listdir(pkgdest)
+        self.assertIn("__init__.py", files)
+        self.assertIn("__init__.pyc", files)
+        self.assertIn("README.txt", files)
+
+    def test_empty_package_dir(self):
+        # See SF 1668596/1720897.
+        cwd = os.getcwd()
+
+        # create the distribution files.
+        sources = self.mkdtemp()
+        pkg = os.path.join(sources, 'pkg')
+        os.mkdir(pkg)
+        open(os.path.join(pkg, "__init__.py"), "w").close()
+        testdir = os.path.join(pkg, "doc")
+        os.mkdir(testdir)
+        open(os.path.join(testdir, "testfile"), "w").close()
+
+        os.chdir(sources)
+        old_stdout = sys.stdout
+        #sys.stdout = StringIO.StringIO()
+
+        try:
+            dist = Distribution({"packages": ["pkg"],
+                                 "package_dir": sources,
+                                 "package_data": {"pkg": ["doc/*"]}})
+            # script_name need not exist, it just need to be initialized
+            dist.script_name = os.path.join(sources, "setup.py")
+            dist.script_args = ["build"]
+            dist.parse_command_line()
+
+            try:
+                dist.run_commands()
+            except PackagingFileError as e:
+                self.fail("failed package_data test when package_dir is ''")
+        finally:
+            # Restore state.
+            os.chdir(cwd)
+            sys.stdout = old_stdout
+
+    @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'),
+                         'sys.dont_write_bytecode not supported')
+    def test_dont_write_bytecode(self):
+        # makes sure byte_compile is not used
+        pkg_dir, dist = self.create_dist()
+        cmd = build_py(dist)
+        cmd.compile = True
+        cmd.optimize = 1
+
+        old_dont_write_bytecode = sys.dont_write_bytecode
+        sys.dont_write_bytecode = True
+        try:
+            cmd.byte_compile([])
+        finally:
+            sys.dont_write_bytecode = old_dont_write_bytecode
+
+        self.assertIn('byte-compiling is disabled', self.get_logs()[0])
+
+def test_suite():
+    return unittest.makeSuite(BuildPyTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_build_scripts.py b/Lib/packaging/tests/test_command_build_scripts.py
new file mode 100644
index 0000000..60d8b68
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build_scripts.py
@@ -0,0 +1,112 @@
+"""Tests for distutils.command.build_scripts."""
+
+import os
+import sys
+import sysconfig
+from packaging.dist import Distribution
+from packaging.command.build_scripts import build_scripts
+
+from packaging.tests import unittest, support
+
+
+class BuildScriptsTestCase(support.TempdirManager,
+                           support.LoggingCatcher,
+                           unittest.TestCase):
+
+    def test_default_settings(self):
+        cmd = self.get_build_scripts_cmd("/foo/bar", [])
+        self.assertFalse(cmd.force)
+        self.assertIs(cmd.build_dir, None)
+
+        cmd.finalize_options()
+
+        self.assertTrue(cmd.force)
+        self.assertEqual(cmd.build_dir, "/foo/bar")
+
+    def test_build(self):
+        source = self.mkdtemp()
+        target = self.mkdtemp()
+        expected = self.write_sample_scripts(source)
+
+        cmd = self.get_build_scripts_cmd(target,
+                                         [os.path.join(source, fn)
+                                          for fn in expected])
+        cmd.finalize_options()
+        cmd.run()
+
+        built = os.listdir(target)
+        for name in expected:
+            self.assertIn(name, built)
+
+    def get_build_scripts_cmd(self, target, scripts):
+        dist = Distribution()
+        dist.scripts = scripts
+        dist.command_obj["build"] = support.DummyCommand(
+            build_scripts=target,
+            force=True,
+            executable=sys.executable,
+            use_2to3=False,
+            use_2to3_fixers=None,
+            convert_2to3_doctests=None
+            )
+        return build_scripts(dist)
+
+    def write_sample_scripts(self, dir):
+        expected = []
+        expected.append("script1.py")
+        self.write_script(dir, "script1.py",
+                          ("#! /usr/bin/env python2.3\n"
+                           "# bogus script w/ Python sh-bang\n"
+                           "pass\n"))
+        expected.append("script2.py")
+        self.write_script(dir, "script2.py",
+                          ("#!/usr/bin/python\n"
+                           "# bogus script w/ Python sh-bang\n"
+                           "pass\n"))
+        expected.append("shell.sh")
+        self.write_script(dir, "shell.sh",
+                          ("#!/bin/sh\n"
+                           "# bogus shell script w/ sh-bang\n"
+                           "exit 0\n"))
+        return expected
+
+    def write_script(self, dir, name, text):
+        f = open(os.path.join(dir, name), "w")
+        try:
+            f.write(text)
+        finally:
+            f.close()
+
+    def test_version_int(self):
+        source = self.mkdtemp()
+        target = self.mkdtemp()
+        expected = self.write_sample_scripts(source)
+
+
+        cmd = self.get_build_scripts_cmd(target,
+                                         [os.path.join(source, fn)
+                                          for fn in expected])
+        cmd.finalize_options()
+
+        # http://bugs.python.org/issue4524
+        #
+        # On linux-g++-32 with command line `./configure --enable-ipv6
+        # --with-suffix=3`, python is compiled okay but the build scripts
+        # failed when writing the name of the executable
+        old = sysconfig.get_config_vars().get('VERSION')
+        sysconfig._CONFIG_VARS['VERSION'] = 4
+        try:
+            cmd.run()
+        finally:
+            if old is not None:
+                sysconfig._CONFIG_VARS['VERSION'] = old
+
+        built = os.listdir(target)
+        for name in expected:
+            self.assertIn(name, built)
+
+def test_suite():
+    return unittest.makeSuite(BuildScriptsTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_check.py b/Lib/packaging/tests/test_command_check.py
new file mode 100644
index 0000000..8b32673
--- /dev/null
+++ b/Lib/packaging/tests/test_command_check.py
@@ -0,0 +1,131 @@
+"""Tests for distutils.command.check."""
+
+import logging
+from packaging.command.check import check
+from packaging.metadata import _HAS_DOCUTILS
+from packaging.errors import PackagingSetupError, MetadataMissingError
+from packaging.tests import unittest, support
+
+
+class CheckTestCase(support.LoggingCatcher,
+                    support.TempdirManager,
+                    unittest.TestCase):
+
+    def _run(self, metadata=None, **options):
+        if metadata is None:
+            metadata = {'name': 'xxx', 'version': '1.2'}
+        pkg_info, dist = self.create_dist(**metadata)
+        cmd = check(dist)
+        cmd.initialize_options()
+        for name, value in options.items():
+            setattr(cmd, name, value)
+        cmd.ensure_finalized()
+        cmd.run()
+        return cmd
+
+    def test_check_metadata(self):
+        # let's run the command with no metadata at all
+        # by default, check is checking the metadata
+        # should have some warnings
+        cmd = self._run()
+        # trick: using assertNotEqual with an empty list will give us a more
+        # useful error message than assertGreater(.., 0) when the code change
+        # and the test fails
+        self.assertNotEqual([], self.get_logs(logging.WARNING))
+
+        # now let's add the required fields
+        # and run it again, to make sure we don't get
+        # any warning anymore
+        self.loghandler.flush()
+        metadata = {'home_page': 'xxx', 'author': 'xxx',
+                    'author_email': 'xxx',
+                    'name': 'xxx', 'version': '4.2',
+                    }
+        cmd = self._run(metadata)
+        self.assertEqual([], self.get_logs(logging.WARNING))
+
+        # now with the strict mode, we should
+        # get an error if there are missing metadata
+        self.assertRaises(MetadataMissingError, self._run, {}, **{'strict': 1})
+        self.assertRaises(PackagingSetupError, self._run,
+            {'name': 'xxx', 'version': 'xxx'}, **{'strict': 1})
+
+        # and of course, no error when all metadata fields are present
+        self.loghandler.flush()
+        cmd = self._run(metadata, strict=True)
+        self.assertEqual([], self.get_logs(logging.WARNING))
+
+    def test_check_metadata_1_2(self):
+        # let's run the command with no metadata at all
+        # by default, check is checking the metadata
+        # should have some warnings
+        cmd = self._run()
+        self.assertNotEqual([], self.get_logs(logging.WARNING))
+
+        # now let's add the required fields and run it again, to make sure we
+        # don't get any warning anymore let's use requires_python as a marker
+        # to enforce Metadata-Version 1.2
+        metadata = {'home_page': 'xxx', 'author': 'xxx',
+                    'author_email': 'xxx',
+                    'name': 'xxx', 'version': '4.2',
+                    'requires_python': '2.4',
+                    }
+        self.loghandler.flush()
+        cmd = self._run(metadata)
+        self.assertEqual([], self.get_logs(logging.WARNING))
+
+        # now with the strict mode, we should
+        # get an error if there are missing metadata
+        self.assertRaises(MetadataMissingError, self._run, {}, **{'strict': 1})
+        self.assertRaises(PackagingSetupError, self._run,
+            {'name': 'xxx', 'version': 'xxx'}, **{'strict': 1})
+
+        # complain about version format
+        metadata['version'] = 'xxx'
+        self.assertRaises(PackagingSetupError, self._run, metadata,
+            **{'strict': 1})
+
+        # now with correct version format again
+        metadata['version'] = '4.2'
+        self.loghandler.flush()
+        cmd = self._run(metadata, strict=True)
+        self.assertEqual([], self.get_logs(logging.WARNING))
+
+    @unittest.skipUnless(_HAS_DOCUTILS, "requires docutils")
+    def test_check_restructuredtext(self):
+        # let's see if it detects broken rest in long_description
+        broken_rest = 'title\n===\n\ntest'
+        pkg_info, dist = self.create_dist(description=broken_rest)
+        cmd = check(dist)
+        cmd.check_restructuredtext()
+        self.assertEqual(len(self.get_logs(logging.WARNING)), 1)
+
+        self.loghandler.flush()
+        pkg_info, dist = self.create_dist(description='title\n=====\n\ntest')
+        cmd = check(dist)
+        cmd.check_restructuredtext()
+        self.assertEqual([], self.get_logs(logging.WARNING))
+
+    def test_check_all(self):
+        self.assertRaises(PackagingSetupError, self._run,
+                          {'name': 'xxx', 'version': 'xxx'}, **{'strict': 1,
+                                 'all': 1})
+        self.assertRaises(MetadataMissingError, self._run,
+                          {}, **{'strict': 1,
+                                 'all': 1})
+
+    def test_check_hooks(self):
+        pkg_info, dist = self.create_dist()
+        dist.command_options['install_dist'] = {
+            'pre_hook': ('file', {"a": 'some.nonextistant.hook.ghrrraarrhll'}),
+        }
+        cmd = check(dist)
+        cmd.check_hooks_resolvable()
+        self.assertEqual(len(self.get_logs(logging.WARNING)), 1)
+
+
+def test_suite():
+    return unittest.makeSuite(CheckTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_clean.py b/Lib/packaging/tests/test_command_clean.py
new file mode 100644
index 0000000..8d29e4d
--- /dev/null
+++ b/Lib/packaging/tests/test_command_clean.py
@@ -0,0 +1,48 @@
+"""Tests for distutils.command.clean."""
+import os
+
+from packaging.command.clean import clean
+from packaging.tests import unittest, support
+
+
+class cleanTestCase(support.TempdirManager, support.LoggingCatcher,
+                    unittest.TestCase):
+
+    def test_simple_run(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = clean(dist)
+
+        # let's add some elements clean should remove
+        dirs = [(d, os.path.join(pkg_dir, d))
+                for d in ('build_temp', 'build_lib', 'bdist_base',
+                'build_scripts', 'build_base')]
+
+        for name, path in dirs:
+            os.mkdir(path)
+            setattr(cmd, name, path)
+            if name == 'build_base':
+                continue
+            for f in ('one', 'two', 'three'):
+                self.write_file(os.path.join(path, f))
+
+        # let's run the command
+        cmd.all = True
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # make sure the files where removed
+        for name, path in dirs:
+            self.assertFalse(os.path.exists(path),
+                             '%r was not removed' % path)
+
+        # let's run the command again (should spit warnings but succeed)
+        cmd.all = True
+        cmd.ensure_finalized()
+        cmd.run()
+
+
+def test_suite():
+    return unittest.makeSuite(cleanTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_cmd.py b/Lib/packaging/tests/test_command_cmd.py
new file mode 100644
index 0000000..8ac9dce
--- /dev/null
+++ b/Lib/packaging/tests/test_command_cmd.py
@@ -0,0 +1,101 @@
+"""Tests for distutils.cmd."""
+import os
+
+from packaging.command.cmd import Command
+from packaging.dist import Distribution
+from packaging.errors import PackagingOptionError
+from packaging.tests import support, unittest
+
+
+class MyCmd(Command):
+    def initialize_options(self):
+        pass
+
+
+class CommandTestCase(support.LoggingCatcher,
+                      unittest.TestCase):
+
+    def setUp(self):
+        super(CommandTestCase, self).setUp()
+        dist = Distribution()
+        self.cmd = MyCmd(dist)
+
+    def test_make_file(self):
+        cmd = self.cmd
+
+        # making sure it raises when infiles is not a string or a list/tuple
+        self.assertRaises(TypeError, cmd.make_file,
+                          infiles=1, outfile='', func='func', args=())
+
+        # making sure execute gets called properly
+        def _execute(func, args, exec_msg, level):
+            self.assertEqual(exec_msg, 'generating out from in')
+        cmd.force = True
+        cmd.execute = _execute
+        cmd.make_file(infiles='in', outfile='out', func='func', args=())
+
+    def test_dump_options(self):
+        cmd = self.cmd
+        cmd.option1 = 1
+        cmd.option2 = 1
+        cmd.user_options = [('option1', '', ''), ('option2', '', '')]
+        cmd.dump_options()
+
+        wanted = ["command options for 'MyCmd':", '  option1 = 1',
+                  '  option2 = 1']
+        msgs = self.get_logs()
+        self.assertEqual(msgs, wanted)
+
+    def test_ensure_string(self):
+        cmd = self.cmd
+        cmd.option1 = 'ok'
+        cmd.ensure_string('option1')
+
+        cmd.option2 = None
+        cmd.ensure_string('option2', 'xxx')
+        self.assertTrue(hasattr(cmd, 'option2'))
+
+        cmd.option3 = 1
+        self.assertRaises(PackagingOptionError, cmd.ensure_string, 'option3')
+
+    def test_ensure_string_list(self):
+        cmd = self.cmd
+        cmd.option1 = 'ok,dok'
+        cmd.ensure_string_list('option1')
+        self.assertEqual(cmd.option1, ['ok', 'dok'])
+
+        cmd.yes_string_list = ['one', 'two', 'three']
+        cmd.yes_string_list2 = 'ok'
+        cmd.ensure_string_list('yes_string_list')
+        cmd.ensure_string_list('yes_string_list2')
+        self.assertEqual(cmd.yes_string_list, ['one', 'two', 'three'])
+        self.assertEqual(cmd.yes_string_list2, ['ok'])
+
+        cmd.not_string_list = ['one', 2, 'three']
+        cmd.not_string_list2 = object()
+        self.assertRaises(PackagingOptionError,
+                          cmd.ensure_string_list, 'not_string_list')
+
+        self.assertRaises(PackagingOptionError,
+                          cmd.ensure_string_list, 'not_string_list2')
+
+    def test_ensure_filename(self):
+        cmd = self.cmd
+        cmd.option1 = __file__
+        cmd.ensure_filename('option1')
+        cmd.option2 = 'xxx'
+        self.assertRaises(PackagingOptionError, cmd.ensure_filename, 'option2')
+
+    def test_ensure_dirname(self):
+        cmd = self.cmd
+        cmd.option1 = os.path.dirname(__file__) or os.curdir
+        cmd.ensure_dirname('option1')
+        cmd.option2 = 'xxx'
+        self.assertRaises(PackagingOptionError, cmd.ensure_dirname, 'option2')
+
+
+def test_suite():
+    return unittest.makeSuite(CommandTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_config.py b/Lib/packaging/tests/test_command_config.py
new file mode 100644
index 0000000..6d780c5
--- /dev/null
+++ b/Lib/packaging/tests/test_command_config.py
@@ -0,0 +1,76 @@
+"""Tests for distutils.command.config."""
+import os
+import sys
+import logging
+
+from packaging.command.config import dump_file, config
+from packaging.tests import unittest, support
+
+
+class ConfigTestCase(support.LoggingCatcher,
+                     support.TempdirManager,
+                     unittest.TestCase):
+
+    def test_dump_file(self):
+        this_file = __file__.rstrip('co')
+        with open(this_file) as f:
+            numlines = len(f.readlines())
+
+        dump_file(this_file, 'I am the header')
+
+        logs = []
+        for log in self.get_logs(logging.INFO):
+            logs.extend(line for line in log.split('\n'))
+        self.assertEqual(len(logs), numlines + 2)
+
+    @unittest.skipIf(sys.platform == 'win32', 'disabled on win32')
+    def test_search_cpp(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = config(dist)
+
+        # simple pattern searches
+        match = cmd.search_cpp(pattern='xxx', body='// xxx')
+        self.assertEqual(match, 0)
+
+        match = cmd.search_cpp(pattern='_configtest', body='// xxx')
+        self.assertEqual(match, 1)
+
+    def test_finalize_options(self):
+        # finalize_options does a bit of transformation
+        # on options
+        pkg_dir, dist = self.create_dist()
+        cmd = config(dist)
+        cmd.include_dirs = 'one%stwo' % os.pathsep
+        cmd.libraries = 'one'
+        cmd.library_dirs = 'three%sfour' % os.pathsep
+        cmd.ensure_finalized()
+
+        self.assertEqual(cmd.include_dirs, ['one', 'two'])
+        self.assertEqual(cmd.libraries, ['one'])
+        self.assertEqual(cmd.library_dirs, ['three', 'four'])
+
+    def test_clean(self):
+        # _clean removes files
+        tmp_dir = self.mkdtemp()
+        f1 = os.path.join(tmp_dir, 'one')
+        f2 = os.path.join(tmp_dir, 'two')
+
+        self.write_file(f1, 'xxx')
+        self.write_file(f2, 'xxx')
+
+        for f in (f1, f2):
+            self.assertTrue(os.path.exists(f))
+
+        pkg_dir, dist = self.create_dist()
+        cmd = config(dist)
+        cmd._clean(f1, f2)
+
+        for f in (f1, f2):
+            self.assertFalse(os.path.exists(f))
+
+
+def test_suite():
+    return unittest.makeSuite(ConfigTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_data.py b/Lib/packaging/tests/test_command_install_data.py
new file mode 100644
index 0000000..8b8bbac
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_data.py
@@ -0,0 +1,80 @@
+"""Tests for packaging.command.install_data."""
+import os
+import sysconfig
+from sysconfig import _get_default_scheme
+from packaging.tests import unittest, support
+from packaging.command.install_data import install_data
+
+
+class InstallDataTestCase(support.TempdirManager,
+                          support.LoggingCatcher,
+                          unittest.TestCase):
+
+    def test_simple_run(self):
+        self.addCleanup(setattr, sysconfig, '_SCHEMES', sysconfig._SCHEMES)
+
+        pkg_dir, dist = self.create_dist()
+        cmd = install_data(dist)
+        cmd.install_dir = inst = os.path.join(pkg_dir, 'inst')
+
+        sysconfig._SCHEMES.set(_get_default_scheme(), 'inst',
+                               os.path.join(pkg_dir, 'inst'))
+        sysconfig._SCHEMES.set(_get_default_scheme(), 'inst2',
+                               os.path.join(pkg_dir, 'inst2'))
+
+        one = os.path.join(pkg_dir, 'one')
+        self.write_file(one, 'xxx')
+        inst2 = os.path.join(pkg_dir, 'inst2')
+        two = os.path.join(pkg_dir, 'two')
+        self.write_file(two, 'xxx')
+
+        cmd.data_files = {one: '{inst}/one', two: '{inst2}/two'}
+        self.assertCountEqual(cmd.get_inputs(), [one, two])
+
+        # let's run the command
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # let's check the result
+        self.assertEqual(len(cmd.get_outputs()), 2)
+        rtwo = os.path.split(two)[-1]
+        self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
+        rone = os.path.split(one)[-1]
+        self.assertTrue(os.path.exists(os.path.join(inst, rone)))
+        cmd.outfiles = []
+
+        # let's try with warn_dir one
+        cmd.warn_dir = True
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # let's check the result
+        self.assertEqual(len(cmd.get_outputs()), 2)
+        self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
+        self.assertTrue(os.path.exists(os.path.join(inst, rone)))
+        cmd.outfiles = []
+
+        # now using root and empty dir
+        cmd.root = os.path.join(pkg_dir, 'root')
+        three = os.path.join(cmd.install_dir, 'three')
+        self.write_file(three, 'xx')
+
+        sysconfig._SCHEMES.set(_get_default_scheme(), 'inst3',
+                               cmd.install_dir)
+
+        cmd.data_files = {one: '{inst}/one', two: '{inst2}/two',
+                          three: '{inst3}/three'}
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # let's check the result
+        self.assertEqual(len(cmd.get_outputs()), 3)
+        self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
+        self.assertTrue(os.path.exists(os.path.join(inst, rone)))
+
+
+def test_suite():
+    return unittest.makeSuite(InstallDataTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_dist.py b/Lib/packaging/tests/test_command_install_dist.py
new file mode 100644
index 0000000..a06d1f6
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_dist.py
@@ -0,0 +1,210 @@
+"""Tests for packaging.command.install."""
+
+import os
+import sys
+
+from sysconfig import (get_scheme_names, get_config_vars,
+                       _SCHEMES, get_config_var, get_path)
+
+_CONFIG_VARS = get_config_vars()
+
+from packaging.tests import captured_stdout
+
+from packaging.command.install_dist import install_dist
+from packaging.command import install_dist as install_module
+from packaging.dist import Distribution
+from packaging.errors import PackagingOptionError
+
+from packaging.tests import unittest, support
+
+
+class InstallTestCase(support.TempdirManager,
+                      support.LoggingCatcher,
+                      unittest.TestCase):
+
+    def test_home_installation_scheme(self):
+        # This ensure two things:
+        # - that --home generates the desired set of directory names
+        # - test --home is supported on all platforms
+        builddir = self.mkdtemp()
+        destination = os.path.join(builddir, "installation")
+
+        dist = Distribution({"name": "foopkg"})
+        # script_name need not exist, it just need to be initialized
+        dist.script_name = os.path.join(builddir, "setup.py")
+        dist.command_obj["build"] = support.DummyCommand(
+            build_base=builddir,
+            build_lib=os.path.join(builddir, "lib"),
+        )
+
+        old_posix_prefix = _SCHEMES.get('posix_prefix', 'platinclude')
+        old_posix_home = _SCHEMES.get('posix_home', 'platinclude')
+
+        new_path = '{platbase}/include/python{py_version_short}'
+        _SCHEMES.set('posix_prefix', 'platinclude', new_path)
+        _SCHEMES.set('posix_home', 'platinclude', '{platbase}/include/python')
+
+        try:
+            cmd = install_dist(dist)
+            cmd.home = destination
+            cmd.ensure_finalized()
+        finally:
+            _SCHEMES.set('posix_prefix', 'platinclude', old_posix_prefix)
+            _SCHEMES.set('posix_home', 'platinclude', old_posix_home)
+
+        self.assertEqual(cmd.install_base, destination)
+        self.assertEqual(cmd.install_platbase, destination)
+
+        def check_path(got, expected):
+            got = os.path.normpath(got)
+            expected = os.path.normpath(expected)
+            self.assertEqual(got, expected)
+
+        libdir = os.path.join(destination, "lib", "python")
+        check_path(cmd.install_lib, libdir)
+        check_path(cmd.install_platlib, libdir)
+        check_path(cmd.install_purelib, libdir)
+        check_path(cmd.install_headers,
+                   os.path.join(destination, "include", "python", "foopkg"))
+        check_path(cmd.install_scripts, os.path.join(destination, "bin"))
+        check_path(cmd.install_data, destination)
+
+    @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+    def test_user_site(self):
+        # test install with --user
+        # preparing the environment for the test
+        self.old_user_base = get_config_var('userbase')
+        self.old_user_site = get_path('purelib', '%s_user' % os.name)
+        self.tmpdir = self.mkdtemp()
+        self.user_base = os.path.join(self.tmpdir, 'B')
+        self.user_site = os.path.join(self.tmpdir, 'S')
+        _CONFIG_VARS['userbase'] = self.user_base
+        scheme = '%s_user' % os.name
+        _SCHEMES.set(scheme, 'purelib', self.user_site)
+
+        def _expanduser(path):
+            if path[0] == '~':
+                path = os.path.normpath(self.tmpdir) + path[1:]
+            return path
+
+        self.old_expand = os.path.expanduser
+        os.path.expanduser = _expanduser
+
+        try:
+            # this is the actual test
+            self._test_user_site()
+        finally:
+            _CONFIG_VARS['userbase'] = self.old_user_base
+            _SCHEMES.set(scheme, 'purelib', self.old_user_site)
+            os.path.expanduser = self.old_expand
+
+    def _test_user_site(self):
+        schemes = get_scheme_names()
+        for key in ('nt_user', 'posix_user', 'os2_home'):
+            self.assertIn(key, schemes)
+
+        dist = Distribution({'name': 'xx'})
+        cmd = install_dist(dist)
+        # making sure the user option is there
+        options = [name for name, short, lable in
+                   cmd.user_options]
+        self.assertIn('user', options)
+
+        # setting a value
+        cmd.user = True
+
+        # user base and site shouldn't be created yet
+        self.assertFalse(os.path.exists(self.user_base))
+        self.assertFalse(os.path.exists(self.user_site))
+
+        # let's run finalize
+        cmd.ensure_finalized()
+
+        # now they should
+        self.assertTrue(os.path.exists(self.user_base))
+        self.assertTrue(os.path.exists(self.user_site))
+
+        self.assertIn('userbase', cmd.config_vars)
+        self.assertIn('usersite', cmd.config_vars)
+
+    def test_handle_extra_path(self):
+        dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'})
+        cmd = install_dist(dist)
+
+        # two elements
+        cmd.handle_extra_path()
+        self.assertEqual(cmd.extra_path, ['path', 'dirs'])
+        self.assertEqual(cmd.extra_dirs, 'dirs')
+        self.assertEqual(cmd.path_file, 'path')
+
+        # one element
+        cmd.extra_path = ['path']
+        cmd.handle_extra_path()
+        self.assertEqual(cmd.extra_path, ['path'])
+        self.assertEqual(cmd.extra_dirs, 'path')
+        self.assertEqual(cmd.path_file, 'path')
+
+        # none
+        dist.extra_path = cmd.extra_path = None
+        cmd.handle_extra_path()
+        self.assertEqual(cmd.extra_path, None)
+        self.assertEqual(cmd.extra_dirs, '')
+        self.assertEqual(cmd.path_file, None)
+
+        # three elements (no way !)
+        cmd.extra_path = 'path,dirs,again'
+        self.assertRaises(PackagingOptionError, cmd.handle_extra_path)
+
+    def test_finalize_options(self):
+        dist = Distribution({'name': 'xx'})
+        cmd = install_dist(dist)
+
+        # must supply either prefix/exec-prefix/home or
+        # install-base/install-platbase -- not both
+        cmd.prefix = 'prefix'
+        cmd.install_base = 'base'
+        self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+        # must supply either home or prefix/exec-prefix -- not both
+        cmd.install_base = None
+        cmd.home = 'home'
+        self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+        if sys.version >= '2.6':
+            # can't combine user with with prefix/exec_prefix/home or
+            # install_(plat)base
+            cmd.prefix = None
+            cmd.user = 'user'
+            self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+    def test_old_record(self):
+        # test pre-PEP 376 --record option (outside dist-info dir)
+        install_dir = self.mkdtemp()
+        pkgdir, dist = self.create_dist()
+
+        dist = Distribution()
+        cmd = install_dist(dist)
+        dist.command_obj['install_dist'] = cmd
+        cmd.root = install_dir
+        cmd.record = os.path.join(pkgdir, 'filelist')
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # let's check the record file was created with four
+        # lines, one for each .dist-info entry: METADATA,
+        # INSTALLER, REQUSTED, RECORD
+        f = open(cmd.record)
+        try:
+            self.assertEqual(len(f.readlines()), 4)
+        finally:
+            f.close()
+
+        # XXX test that fancy_getopt is okay with options named
+        # record and no-record but unrelated
+
+
+def test_suite():
+    return unittest.makeSuite(InstallTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_distinfo.py b/Lib/packaging/tests/test_command_install_distinfo.py
new file mode 100644
index 0000000..3d33691
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_distinfo.py
@@ -0,0 +1,192 @@
+"""Tests for ``packaging.command.install_distinfo``. """
+
+import os
+import csv
+import hashlib
+import sys
+
+from packaging.command.install_distinfo import install_distinfo
+from packaging.command.cmd import Command
+from packaging.metadata import Metadata
+from packaging.tests import unittest, support
+
+
+class DummyInstallCmd(Command):
+
+    def __init__(self, dist=None):
+        self.outputs = []
+        self.distribution = dist
+
+    def __getattr__(self, name):
+        return None
+
+    def ensure_finalized(self):
+        pass
+
+    def get_outputs(self):
+        return (self.outputs +
+                self.get_finalized_command('install_distinfo').get_outputs())
+
+
+class InstallDistinfoTestCase(support.TempdirManager,
+                              support.LoggingCatcher,
+                              unittest.TestCase):
+
+    checkLists = lambda self, x, y: self.assertListEqual(sorted(x), sorted(y))
+
+    def test_empty_install(self):
+        pkg_dir, dist = self.create_dist(name='foo',
+                                         version='1.0')
+        install_dir = self.mkdtemp()
+
+        install = DummyInstallCmd(dist)
+        dist.command_obj['install_dist'] = install
+
+        cmd = install_distinfo(dist)
+        dist.command_obj['install_distinfo'] = cmd
+
+        cmd.initialize_options()
+        cmd.distinfo_dir = install_dir
+        cmd.ensure_finalized()
+        cmd.run()
+
+        self.checkLists(os.listdir(install_dir), ['foo-1.0.dist-info'])
+
+        dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+        self.checkLists(os.listdir(dist_info),
+                        ['METADATA', 'RECORD', 'REQUESTED', 'INSTALLER'])
+        with open(os.path.join(dist_info, 'INSTALLER')) as fp:
+            self.assertEqual(fp.read(), 'distutils')
+        with open(os.path.join(dist_info, 'REQUESTED')) as fp:
+            self.assertEqual(fp.read(), '')
+        meta_path = os.path.join(dist_info, 'METADATA')
+        self.assertTrue(Metadata(path=meta_path).check())
+
+    def test_installer(self):
+        pkg_dir, dist = self.create_dist(name='foo',
+                                         version='1.0')
+        install_dir = self.mkdtemp()
+
+        install = DummyInstallCmd(dist)
+        dist.command_obj['install_dist'] = install
+
+        cmd = install_distinfo(dist)
+        dist.command_obj['install_distinfo'] = cmd
+
+        cmd.initialize_options()
+        cmd.distinfo_dir = install_dir
+        cmd.installer = 'bacon-python'
+        cmd.ensure_finalized()
+        cmd.run()
+
+        dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+        with open(os.path.join(dist_info, 'INSTALLER')) as fp:
+            self.assertEqual(fp.read(), 'bacon-python')
+
+    def test_requested(self):
+        pkg_dir, dist = self.create_dist(name='foo',
+                                         version='1.0')
+        install_dir = self.mkdtemp()
+
+        install = DummyInstallCmd(dist)
+        dist.command_obj['install_dist'] = install
+
+        cmd = install_distinfo(dist)
+        dist.command_obj['install_distinfo'] = cmd
+
+        cmd.initialize_options()
+        cmd.distinfo_dir = install_dir
+        cmd.requested = False
+        cmd.ensure_finalized()
+        cmd.run()
+
+        dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+        self.checkLists(os.listdir(dist_info),
+                        ['METADATA', 'RECORD', 'INSTALLER'])
+
+    def test_no_record(self):
+        pkg_dir, dist = self.create_dist(name='foo',
+                                         version='1.0')
+        install_dir = self.mkdtemp()
+
+        install = DummyInstallCmd(dist)
+        dist.command_obj['install_dist'] = install
+
+        cmd = install_distinfo(dist)
+        dist.command_obj['install_distinfo'] = cmd
+
+        cmd.initialize_options()
+        cmd.distinfo_dir = install_dir
+        cmd.no_record = True
+        cmd.ensure_finalized()
+        cmd.run()
+
+        dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+        self.checkLists(os.listdir(dist_info),
+                        ['METADATA', 'REQUESTED', 'INSTALLER'])
+
+    def test_record(self):
+        pkg_dir, dist = self.create_dist(name='foo',
+                                         version='1.0')
+        install_dir = self.mkdtemp()
+
+        install = DummyInstallCmd(dist)
+        dist.command_obj['install_dist'] = install
+
+        fake_dists = os.path.join(os.path.dirname(__file__), 'fake_dists')
+        fake_dists = os.path.realpath(fake_dists)
+
+        # for testing, we simply add all files from _backport's fake_dists
+        dirs = []
+        for dir in os.listdir(fake_dists):
+            full_path = os.path.join(fake_dists, dir)
+            if (not dir.endswith('.egg') or dir.endswith('.egg-info') or
+                dir.endswith('.dist-info')) and os.path.isdir(full_path):
+                dirs.append(full_path)
+
+        for dir in dirs:
+            for path, subdirs, files in os.walk(dir):
+                install.outputs += [os.path.join(path, f) for f in files]
+                install.outputs += [os.path.join('path', f + 'c')
+                                    for f in files if f.endswith('.py')]
+
+        cmd = install_distinfo(dist)
+        dist.command_obj['install_distinfo'] = cmd
+
+        cmd.initialize_options()
+        cmd.distinfo_dir = install_dir
+        cmd.ensure_finalized()
+        cmd.run()
+
+        dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+
+        expected = []
+        for f in install.get_outputs():
+            if (f.endswith('.pyc') or f == os.path.join(
+                install_dir, 'foo-1.0.dist-info', 'RECORD')):
+                expected.append([f, '', ''])
+            else:
+                size = os.path.getsize(f)
+                md5 = hashlib.md5()
+                with open(f) as fp:
+                    md5.update(fp.read().encode())
+                hash = md5.hexdigest()
+                expected.append([f, hash, str(size)])
+
+        parsed = []
+        with open(os.path.join(dist_info, 'RECORD'), 'r') as f:
+            reader = csv.reader(f, delimiter=',',
+                                   lineterminator=os.linesep,
+                                   quotechar='"')
+            parsed = list(reader)
+
+        self.maxDiff = None
+        self.checkLists(parsed, expected)
+
+
+def test_suite():
+    return unittest.makeSuite(InstallDistinfoTestCase)
+
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_headers.py b/Lib/packaging/tests/test_command_install_headers.py
new file mode 100644
index 0000000..f2906a7
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_headers.py
@@ -0,0 +1,38 @@
+"""Tests for packaging.command.install_headers."""
+import os
+
+from packaging.command.install_headers import install_headers
+from packaging.tests import unittest, support
+
+
+class InstallHeadersTestCase(support.TempdirManager,
+                             support.LoggingCatcher,
+                             unittest.TestCase):
+
+    def test_simple_run(self):
+        # we have two headers
+        header_list = self.mkdtemp()
+        header1 = os.path.join(header_list, 'header1')
+        header2 = os.path.join(header_list, 'header2')
+        self.write_file(header1)
+        self.write_file(header2)
+        headers = [header1, header2]
+
+        pkg_dir, dist = self.create_dist(headers=headers)
+        cmd = install_headers(dist)
+        self.assertEqual(cmd.get_inputs(), headers)
+
+        # let's run the command
+        cmd.install_dir = os.path.join(pkg_dir, 'inst')
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # let's check the results
+        self.assertEqual(len(cmd.get_outputs()), 2)
+
+
+def test_suite():
+    return unittest.makeSuite(InstallHeadersTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_lib.py b/Lib/packaging/tests/test_command_install_lib.py
new file mode 100644
index 0000000..99d47dd
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_lib.py
@@ -0,0 +1,111 @@
+"""Tests for packaging.command.install_data."""
+import sys
+import os
+
+from packaging.tests import unittest, support
+from packaging.command.install_lib import install_lib
+from packaging.compiler.extension import Extension
+from packaging.errors import PackagingOptionError
+
+try:
+    no_bytecode = sys.dont_write_bytecode
+    bytecode_support = True
+except AttributeError:
+    no_bytecode = False
+    bytecode_support = False
+
+
+class InstallLibTestCase(support.TempdirManager,
+                         support.LoggingCatcher,
+                         support.EnvironRestorer,
+                         unittest.TestCase):
+
+    restore_environ = ['PYTHONPATH']
+
+    def test_finalize_options(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = install_lib(dist)
+
+        cmd.finalize_options()
+        self.assertTrue(cmd.compile)
+        self.assertEqual(cmd.optimize, 0)
+
+        # optimize must be 0, 1, or 2
+        cmd.optimize = 'foo'
+        self.assertRaises(PackagingOptionError, cmd.finalize_options)
+        cmd.optimize = '4'
+        self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+        cmd.optimize = '2'
+        cmd.finalize_options()
+        self.assertEqual(cmd.optimize, 2)
+
+    @unittest.skipIf(no_bytecode, 'byte-compile not supported')
+    def test_byte_compile(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = install_lib(dist)
+        cmd.compile = True
+        cmd.optimize = 1
+
+        f = os.path.join(pkg_dir, 'foo.py')
+        self.write_file(f, '# python file')
+        cmd.byte_compile([f])
+        self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyc')))
+        self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyo')))
+
+    def test_get_outputs(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = install_lib(dist)
+
+        # setting up a dist environment
+        cmd.compile = True
+        cmd.optimize = 1
+        cmd.install_dir = pkg_dir
+        f = os.path.join(pkg_dir, '__init__.py')
+        self.write_file(f, '# python package')
+        cmd.distribution.ext_modules = [Extension('foo', ['xxx'])]
+        cmd.distribution.packages = [pkg_dir]
+        cmd.distribution.script_name = 'setup.py'
+
+        # get_output should return 4 elements
+        self.assertEqual(len(cmd.get_outputs()), 4)
+
+    def test_get_inputs(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = install_lib(dist)
+
+        # setting up a dist environment
+        cmd.compile = True
+        cmd.optimize = 1
+        cmd.install_dir = pkg_dir
+        f = os.path.join(pkg_dir, '__init__.py')
+        self.write_file(f, '# python package')
+        cmd.distribution.ext_modules = [Extension('foo', ['xxx'])]
+        cmd.distribution.packages = [pkg_dir]
+        cmd.distribution.script_name = 'setup.py'
+
+        # get_input should return 2 elements
+        self.assertEqual(len(cmd.get_inputs()), 2)
+
+    @unittest.skipUnless(bytecode_support,
+                         'sys.dont_write_bytecode not supported')
+    def test_dont_write_bytecode(self):
+        # makes sure byte_compile is not used
+        pkg_dir, dist = self.create_dist()
+        cmd = install_lib(dist)
+        cmd.compile = True
+        cmd.optimize = 1
+
+        self.addCleanup(setattr, sys, 'dont_write_bytecode',
+                        sys.dont_write_bytecode)
+        sys.dont_write_bytecode = True
+        cmd.byte_compile([])
+
+        self.assertIn('byte-compiling is disabled', self.get_logs()[0])
+
+
+def test_suite():
+    return unittest.makeSuite(InstallLibTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_scripts.py b/Lib/packaging/tests/test_command_install_scripts.py
new file mode 100644
index 0000000..08c7338
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_scripts.py
@@ -0,0 +1,78 @@
+"""Tests for packaging.command.install_scripts."""
+import os
+
+from packaging.tests import unittest, support
+from packaging.command.install_scripts import install_scripts
+from packaging.dist import Distribution
+
+
+class InstallScriptsTestCase(support.TempdirManager,
+                             support.LoggingCatcher,
+                             unittest.TestCase):
+
+    def test_default_settings(self):
+        dist = Distribution()
+        dist.command_obj["build"] = support.DummyCommand(
+            build_scripts="/foo/bar")
+        dist.command_obj["install_dist"] = support.DummyCommand(
+            install_scripts="/splat/funk",
+            force=True,
+            skip_build=True,
+            )
+        cmd = install_scripts(dist)
+        self.assertFalse(cmd.force)
+        self.assertFalse(cmd.skip_build)
+        self.assertIs(cmd.build_dir, None)
+        self.assertIs(cmd.install_dir, None)
+
+        cmd.finalize_options()
+
+        self.assertTrue(cmd.force)
+        self.assertTrue(cmd.skip_build)
+        self.assertEqual(cmd.build_dir, "/foo/bar")
+        self.assertEqual(cmd.install_dir, "/splat/funk")
+
+    def test_installation(self):
+        source = self.mkdtemp()
+        expected = []
+
+        def write_script(name, text):
+            expected.append(name)
+            f = open(os.path.join(source, name), "w")
+            try:
+                f.write(text)
+            finally:
+                f.close()
+
+        write_script("script1.py", ("#! /usr/bin/env python2.3\n"
+                                    "# bogus script w/ Python sh-bang\n"
+                                    "pass\n"))
+        write_script("script2.py", ("#!/usr/bin/python\n"
+                                    "# bogus script w/ Python sh-bang\n"
+                                    "pass\n"))
+        write_script("shell.sh", ("#!/bin/sh\n"
+                                  "# bogus shell script w/ sh-bang\n"
+                                  "exit 0\n"))
+
+        target = self.mkdtemp()
+        dist = Distribution()
+        dist.command_obj["build"] = support.DummyCommand(build_scripts=source)
+        dist.command_obj["install_dist"] = support.DummyCommand(
+            install_scripts=target,
+            force=True,
+            skip_build=True,
+            )
+        cmd = install_scripts(dist)
+        cmd.finalize_options()
+        cmd.run()
+
+        installed = os.listdir(target)
+        for name in expected:
+            self.assertIn(name, installed)
+
+
+def test_suite():
+    return unittest.makeSuite(InstallScriptsTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_register.py b/Lib/packaging/tests/test_command_register.py
new file mode 100644
index 0000000..7aa487a
--- /dev/null
+++ b/Lib/packaging/tests/test_command_register.py
@@ -0,0 +1,259 @@
+"""Tests for packaging.command.register."""
+import os
+import getpass
+import urllib.request
+import urllib.error
+import urllib.parse
+
+try:
+    import docutils
+    DOCUTILS_SUPPORT = True
+except ImportError:
+    DOCUTILS_SUPPORT = False
+
+from packaging.tests import unittest, support
+from packaging.command import register as register_module
+from packaging.command.register import register
+from packaging.errors import PackagingSetupError
+
+
+PYPIRC_NOPASSWORD = """\
+[distutils]
+
+index-servers =
+    server1
+
+[server1]
+username:me
+"""
+
+WANTED_PYPIRC = """\
+[distutils]
+index-servers =
+    pypi
+
+[pypi]
+username:tarek
+password:password
+"""
+
+
+class Inputs:
+    """Fakes user inputs."""
+    def __init__(self, *answers):
+        self.answers = answers
+        self.index = 0
+
+    def __call__(self, prompt=''):
+        try:
+            return self.answers[self.index]
+        finally:
+            self.index += 1
+
+
+class FakeOpener:
+    """Fakes a PyPI server"""
+    def __init__(self):
+        self.reqs = []
+
+    def __call__(self, *args):
+        return self
+
+    def open(self, req):
+        self.reqs.append(req)
+        return self
+
+    def read(self):
+        return 'xxx'
+
+
+class RegisterTestCase(support.TempdirManager,
+                       support.EnvironRestorer,
+                       support.LoggingCatcher,
+                       unittest.TestCase):
+
+    restore_environ = ['HOME']
+
+    def setUp(self):
+        super(RegisterTestCase, self).setUp()
+        self.tmp_dir = self.mkdtemp()
+        self.rc = os.path.join(self.tmp_dir, '.pypirc')
+        os.environ['HOME'] = self.tmp_dir
+
+        # patching the password prompt
+        self._old_getpass = getpass.getpass
+
+        def _getpass(prompt):
+            return 'password'
+
+        getpass.getpass = _getpass
+        self.old_opener = urllib.request.build_opener
+        self.conn = urllib.request.build_opener = FakeOpener()
+
+    def tearDown(self):
+        getpass.getpass = self._old_getpass
+        urllib.request.build_opener = self.old_opener
+        if hasattr(register_module, 'input'):
+            del register_module.input
+        super(RegisterTestCase, self).tearDown()
+
+    def _get_cmd(self, metadata=None):
+        if metadata is None:
+            metadata = {'url': 'xxx', 'author': 'xxx',
+                        'author_email': 'xxx',
+                        'name': 'xxx', 'version': 'xxx'}
+        pkg_info, dist = self.create_dist(**metadata)
+        return register(dist)
+
+    def test_create_pypirc(self):
+        # this test makes sure a .pypirc file
+        # is created when requested.
+
+        # let's create a register instance
+        cmd = self._get_cmd()
+
+        # we shouldn't have a .pypirc file yet
+        self.assertFalse(os.path.exists(self.rc))
+
+        # patching input and getpass.getpass
+        # so register gets happy
+        # Here's what we are faking :
+        # use your existing login (choice 1.)
+        # Username : 'tarek'
+        # Password : 'password'
+        # Save your login (y/N)? : 'y'
+        inputs = Inputs('1', 'tarek', 'y')
+        register_module.input = inputs
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # we should have a brand new .pypirc file
+        self.assertTrue(os.path.exists(self.rc))
+
+        # with the content similar to WANTED_PYPIRC
+        with open(self.rc) as fp:
+            content = fp.read()
+        self.assertEqual(content, WANTED_PYPIRC)
+
+        # now let's make sure the .pypirc file generated
+        # really works : we shouldn't be asked anything
+        # if we run the command again
+        def _no_way(prompt=''):
+            raise AssertionError(prompt)
+
+        register_module.input = _no_way
+        cmd.show_response = True
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # let's see what the server received : we should
+        # have 2 similar requests
+        self.assertEqual(len(self.conn.reqs), 2)
+        req1 = dict(self.conn.reqs[0].headers)
+        req2 = dict(self.conn.reqs[1].headers)
+        self.assertEqual(req2['Content-length'], req1['Content-length'])
+        self.assertIn('xxx', self.conn.reqs[1].data)
+
+    def test_password_not_in_file(self):
+
+        self.write_file(self.rc, PYPIRC_NOPASSWORD)
+        cmd = self._get_cmd()
+        cmd.finalize_options()
+        cmd._set_config()
+        cmd.send_metadata()
+
+        # dist.password should be set
+        # therefore used afterwards by other commands
+        self.assertEqual(cmd.distribution.password, 'password')
+
+    def test_registration(self):
+        # this test runs choice 2
+        cmd = self._get_cmd()
+        inputs = Inputs('2', 'tarek', 'tarek@ziade.org')
+        register_module.input = inputs
+        # let's run the command
+        # FIXME does this send a real request? use a mock server
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # we should have send a request
+        self.assertEqual(len(self.conn.reqs), 1)
+        req = self.conn.reqs[0]
+        headers = dict(req.headers)
+        self.assertEqual(headers['Content-length'], '608')
+        self.assertIn('tarek', req.data)
+
+    def test_password_reset(self):
+        # this test runs choice 3
+        cmd = self._get_cmd()
+        inputs = Inputs('3', 'tarek@ziade.org')
+        register_module.input = inputs
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # we should have send a request
+        self.assertEqual(len(self.conn.reqs), 1)
+        req = self.conn.reqs[0]
+        headers = dict(req.headers)
+        self.assertEqual(headers['Content-length'], '290')
+        self.assertIn('tarek', req.data)
+
+    @unittest.skipUnless(DOCUTILS_SUPPORT, 'needs docutils')
+    def test_strict(self):
+        # testing the script option
+        # when on, the register command stops if
+        # the metadata is incomplete or if
+        # long_description is not reSt compliant
+
+        # empty metadata
+        cmd = self._get_cmd({'name': 'xxx', 'version': 'xxx'})
+        cmd.ensure_finalized()
+        cmd.strict = True
+        inputs = Inputs('1', 'tarek', 'y')
+        register_module.input = inputs
+        self.assertRaises(PackagingSetupError, cmd.run)
+
+        # metadata is OK but long_description is broken
+        metadata = {'home_page': 'xxx', 'author': 'xxx',
+                    'author_email': 'éxéxé',
+                    'name': 'xxx', 'version': 'xxx',
+                    'description': 'title\n==\n\ntext'}
+
+        cmd = self._get_cmd(metadata)
+        cmd.ensure_finalized()
+        cmd.strict = True
+
+        self.assertRaises(PackagingSetupError, cmd.run)
+
+        # now something that works
+        metadata['description'] = 'title\n=====\n\ntext'
+        cmd = self._get_cmd(metadata)
+        cmd.ensure_finalized()
+        cmd.strict = True
+        inputs = Inputs('1', 'tarek', 'y')
+        register_module.input = inputs
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # strict is not by default
+        cmd = self._get_cmd()
+        cmd.ensure_finalized()
+        inputs = Inputs('1', 'tarek', 'y')
+        register_module.input = inputs
+        cmd.ensure_finalized()
+        cmd.run()
+
+    def test_register_pep345(self):
+        cmd = self._get_cmd({})
+        cmd.ensure_finalized()
+        cmd.distribution.metadata['Requires-Dist'] = ['lxml']
+        data = cmd.build_post_data('submit')
+        self.assertEqual(data['metadata_version'], '1.2')
+        self.assertEqual(data['requires_dist'], ['lxml'])
+
+
+def test_suite():
+    return unittest.makeSuite(RegisterTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_sdist.py b/Lib/packaging/tests/test_command_sdist.py
new file mode 100644
index 0000000..956e258
--- /dev/null
+++ b/Lib/packaging/tests/test_command_sdist.py
@@ -0,0 +1,407 @@
+"""Tests for packaging.command.sdist."""
+import os
+import zipfile
+import tarfile
+import logging
+
+# zlib is not used here, but if it's not available
+# the tests that use zipfile may fail
+try:
+    import zlib
+except ImportError:
+    zlib = None
+
+try:
+    import grp
+    import pwd
+    UID_GID_SUPPORT = True
+except ImportError:
+    UID_GID_SUPPORT = False
+
+from os.path import join
+from packaging.tests import captured_stdout
+from packaging.command.sdist import sdist
+from packaging.command.sdist import show_formats
+from packaging.dist import Distribution
+from packaging.tests import unittest
+from packaging.errors import PackagingOptionError
+from packaging.util import find_executable
+from packaging.tests import support
+from shutil import get_archive_formats
+
+SETUP_PY = """
+from packaging.core import setup
+import somecode
+
+setup(name='fake')
+"""
+
+MANIFEST = """\
+# file GENERATED by packaging, do NOT edit
+README
+inroot.txt
+data%(sep)sdata.dt
+scripts%(sep)sscript.py
+some%(sep)sfile.txt
+some%(sep)sother_file.txt
+somecode%(sep)s__init__.py
+somecode%(sep)sdoc.dat
+somecode%(sep)sdoc.txt
+"""
+
+
+def builder(dist, filelist):
+    filelist.append('bah')
+
+
+class SDistTestCase(support.TempdirManager,
+                    support.LoggingCatcher,
+                    support.EnvironRestorer,
+                    unittest.TestCase):
+
+    restore_environ = ['HOME']
+
+    def setUp(self):
+        # PyPIRCCommandTestCase creates a temp dir already
+        # and put it in self.tmp_dir
+        super(SDistTestCase, self).setUp()
+        self.tmp_dir = self.mkdtemp()
+        os.environ['HOME'] = self.tmp_dir
+        # setting up an environment
+        self.old_path = os.getcwd()
+        os.mkdir(join(self.tmp_dir, 'somecode'))
+        os.mkdir(join(self.tmp_dir, 'dist'))
+        # a package, and a README
+        self.write_file((self.tmp_dir, 'README'), 'xxx')
+        self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#')
+        self.write_file((self.tmp_dir, 'setup.py'), SETUP_PY)
+        os.chdir(self.tmp_dir)
+
+    def tearDown(self):
+        # back to normal
+        os.chdir(self.old_path)
+        super(SDistTestCase, self).tearDown()
+
+    def get_cmd(self, metadata=None):
+        """Returns a cmd"""
+        if metadata is None:
+            metadata = {'name': 'fake', 'version': '1.0',
+                        'url': 'xxx', 'author': 'xxx',
+                        'author_email': 'xxx'}
+        dist = Distribution(metadata)
+        dist.script_name = 'setup.py'
+        dist.packages = ['somecode']
+        dist.include_package_data = True
+        cmd = sdist(dist)
+        cmd.dist_dir = 'dist'
+        return dist, cmd
+
+    @unittest.skipUnless(zlib, "requires zlib")
+    def test_prune_file_list(self):
+        # this test creates a package with some vcs dirs in it
+        # and launch sdist to make sure they get pruned
+        # on all systems
+
+        # creating VCS directories with some files in them
+        os.mkdir(join(self.tmp_dir, 'somecode', '.svn'))
+
+        self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx')
+
+        os.mkdir(join(self.tmp_dir, 'somecode', '.hg'))
+        self.write_file((self.tmp_dir, 'somecode', '.hg',
+                         'ok'), 'xxx')
+
+        os.mkdir(join(self.tmp_dir, 'somecode', '.git'))
+        self.write_file((self.tmp_dir, 'somecode', '.git',
+                         'ok'), 'xxx')
+
+        # now building a sdist
+        dist, cmd = self.get_cmd()
+
+        # zip is available universally
+        # (tar might not be installed under win32)
+        cmd.formats = ['zip']
+
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # now let's check what we have
+        dist_folder = join(self.tmp_dir, 'dist')
+        files = os.listdir(dist_folder)
+        self.assertEqual(files, ['fake-1.0.zip'])
+
+        with zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) as zip_file:
+            content = zip_file.namelist()
+
+        # making sure everything has been pruned correctly
+        self.assertEqual(len(content), 3)
+
+    @unittest.skipUnless(zlib, "requires zlib")
+    @unittest.skipIf(find_executable('tar') is None or
+                     find_executable('gzip') is None,
+                     'requires tar and gzip programs')
+    def test_make_distribution(self):
+        # building a sdist
+        dist, cmd = self.get_cmd()
+
+        # creating a gztar then a tar
+        cmd.formats = ['gztar', 'tar']
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # making sure we have two files
+        dist_folder = join(self.tmp_dir, 'dist')
+        result = sorted(os.listdir(dist_folder))
+        self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
+
+        os.remove(join(dist_folder, 'fake-1.0.tar'))
+        os.remove(join(dist_folder, 'fake-1.0.tar.gz'))
+
+        # now trying a tar then a gztar
+        cmd.formats = ['tar', 'gztar']
+
+        cmd.ensure_finalized()
+        cmd.run()
+
+        result = sorted(os.listdir(dist_folder))
+        self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
+
+    @unittest.skipUnless(zlib, "requires zlib")
+    def test_add_defaults(self):
+
+        # http://bugs.python.org/issue2279
+
+        # add_default should also include
+        # data_files and package_data
+        dist, cmd = self.get_cmd()
+
+        # filling data_files by pointing files
+        # in package_data
+        dist.package_data = {'': ['*.cfg', '*.dat'],
+                             'somecode': ['*.txt']}
+        self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
+        self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#')
+
+        # adding some data in data_files
+        data_dir = join(self.tmp_dir, 'data')
+        os.mkdir(data_dir)
+        self.write_file((data_dir, 'data.dt'), '#')
+        some_dir = join(self.tmp_dir, 'some')
+        os.mkdir(some_dir)
+        self.write_file((self.tmp_dir, 'inroot.txt'), '#')
+        self.write_file((some_dir, 'file.txt'), '#')
+        self.write_file((some_dir, 'other_file.txt'), '#')
+
+        dist.data_files = {'data/data.dt': '{appdata}/data.dt',
+                           'inroot.txt': '{appdata}/inroot.txt',
+                           'some/file.txt': '{appdata}/file.txt',
+                           'some/other_file.txt': '{appdata}/other_file.txt'}
+
+        # adding a script
+        script_dir = join(self.tmp_dir, 'scripts')
+        os.mkdir(script_dir)
+        self.write_file((script_dir, 'script.py'), '#')
+        dist.scripts = [join('scripts', 'script.py')]
+
+        cmd.formats = ['zip']
+        cmd.use_defaults = True
+
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # now let's check what we have
+        dist_folder = join(self.tmp_dir, 'dist')
+        files = os.listdir(dist_folder)
+        self.assertEqual(files, ['fake-1.0.zip'])
+
+        with zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) as zip_file:
+            content = zip_file.namelist()
+
+        # Making sure everything was added. This includes 9 code and data
+        # files in addition to PKG-INFO.
+        self.assertEqual(len(content), 10)
+
+        # Checking the MANIFEST
+        with open(join(self.tmp_dir, 'MANIFEST')) as fp:
+            manifest = fp.read()
+        self.assertEqual(manifest, MANIFEST % {'sep': os.sep})
+
+    @unittest.skipUnless(zlib, "requires zlib")
+    def test_metadata_check_option(self):
+        # testing the `check-metadata` option
+        dist, cmd = self.get_cmd(metadata={'name': 'xxx', 'version': 'xxx'})
+
+        # this should raise some warnings
+        # with the check subcommand
+        cmd.ensure_finalized()
+        cmd.run()
+        warnings = self.get_logs(logging.WARN)
+        self.assertEqual(len(warnings), 3)
+
+        # trying with a complete set of metadata
+        self.loghandler.flush()
+        dist, cmd = self.get_cmd()
+        cmd.ensure_finalized()
+        cmd.metadata_check = False
+        cmd.run()
+        warnings = self.get_logs(logging.WARN)
+        # removing manifest generated warnings
+        warnings = [warn for warn in warnings if
+                    not warn.endswith('-- skipping')]
+        # the remaining warning is about the use of the default file list
+        self.assertEqual(len(warnings), 1)
+
+    def test_show_formats(self):
+        __, stdout = captured_stdout(show_formats)
+
+        # the output should be a header line + one line per format
+        num_formats = len(get_archive_formats())
+        output = [line for line in stdout.split('\n')
+                  if line.strip().startswith('--formats=')]
+        self.assertEqual(len(output), num_formats)
+
+    def test_finalize_options(self):
+
+        dist, cmd = self.get_cmd()
+        cmd.finalize_options()
+
+        # default options set by finalize
+        self.assertEqual(cmd.manifest, 'MANIFEST')
+        self.assertEqual(cmd.dist_dir, 'dist')
+
+        # formats has to be a string splitable on (' ', ',') or
+        # a stringlist
+        cmd.formats = 1
+        self.assertRaises(PackagingOptionError, cmd.finalize_options)
+        cmd.formats = ['zip']
+        cmd.finalize_options()
+
+        # formats has to be known
+        cmd.formats = 'supazipa'
+        self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+    @unittest.skipUnless(zlib, "requires zlib")
+    @unittest.skipUnless(UID_GID_SUPPORT, "requires grp and pwd support")
+    @unittest.skipIf(find_executable('tar') is None or
+                     find_executable('gzip') is None,
+                     'requires tar and gzip programs')
+    def test_make_distribution_owner_group(self):
+        # building a sdist
+        dist, cmd = self.get_cmd()
+
+        # creating a gztar and specifying the owner+group
+        cmd.formats = ['gztar']
+        cmd.owner = pwd.getpwuid(0)[0]
+        cmd.group = grp.getgrgid(0)[0]
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # making sure we have the good rights
+        archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
+        with tarfile.open(archive_name) as archive:
+            for member in archive.getmembers():
+                self.assertEqual(member.uid, 0)
+                self.assertEqual(member.gid, 0)
+
+        # building a sdist again
+        dist, cmd = self.get_cmd()
+
+        # creating a gztar
+        cmd.formats = ['gztar']
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # making sure we have the good rights
+        archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
+        with tarfile.open(archive_name) as archive:
+
+            # note that we are not testing the group ownership here
+            # because, depending on the platforms and the container
+            # rights (see #7408)
+            for member in archive.getmembers():
+                self.assertEqual(member.uid, os.getuid())
+
+    def test_get_file_list(self):
+        # make sure MANIFEST is recalculated
+        dist, cmd = self.get_cmd()
+        # filling data_files by pointing files in package_data
+        dist.package_data = {'somecode': ['*.txt']}
+        self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # Should produce four lines. Those lines are one comment, one default
+        # (README) and two package files.
+        with open(cmd.manifest) as f:
+            manifest = [line.strip() for line in f.read().split('\n')
+                        if line.strip() != '']
+        self.assertEqual(len(manifest), 4)
+
+        # Adding a file
+        self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#')
+
+        # make sure build_py is reinitialized, like a fresh run
+        build_py = dist.get_command_obj('build_py')
+        build_py.finalized = False
+        build_py.ensure_finalized()
+
+        cmd.run()
+
+        with open(cmd.manifest) as f:
+            manifest2 = [line.strip() for line in f.read().split('\n')
+                         if line.strip() != '']
+
+        # Do we have the new file in MANIFEST?
+        self.assertEqual(len(manifest2), 5)
+        self.assertIn('doc2.txt', manifest2[-1])
+
+    def test_manifest_marker(self):
+        # check that autogenerated MANIFESTs have a marker
+        dist, cmd = self.get_cmd()
+        cmd.ensure_finalized()
+        cmd.run()
+
+        with open(cmd.manifest) as f:
+            manifest = [line.strip() for line in f.read().split('\n')
+                        if line.strip() != '']
+
+        self.assertEqual(manifest[0],
+                         '# file GENERATED by packaging, do NOT edit')
+
+    def test_manual_manifest(self):
+        # check that a MANIFEST without a marker is left alone
+        dist, cmd = self.get_cmd()
+        cmd.ensure_finalized()
+        self.write_file((self.tmp_dir, cmd.manifest), 'README.manual')
+        cmd.run()
+
+        with open(cmd.manifest) as f:
+            manifest = [line.strip() for line in f.read().split('\n')
+                        if line.strip() != '']
+
+        self.assertEqual(manifest, ['README.manual'])
+
+    def test_template(self):
+        dist, cmd = self.get_cmd()
+        dist.extra_files = ['include yeah']
+        cmd.ensure_finalized()
+        self.write_file((self.tmp_dir, 'yeah'), 'xxx')
+        cmd.run()
+        with open(cmd.manifest) as f:
+            content = f.read()
+
+        self.assertIn('yeah', content)
+
+    def test_manifest_builder(self):
+        dist, cmd = self.get_cmd()
+        cmd.manifest_builders = 'packaging.tests.test_command_sdist.builder'
+        cmd.ensure_finalized()
+        cmd.run()
+        self.assertIn('bah', cmd.filelist.files)
+
+
+def test_suite():
+    return unittest.makeSuite(SDistTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_test.py b/Lib/packaging/tests/test_command_test.py
new file mode 100644
index 0000000..4fd8452
--- /dev/null
+++ b/Lib/packaging/tests/test_command_test.py
@@ -0,0 +1,225 @@
+import os
+import re
+import sys
+import shutil
+import logging
+import unittest as ut1
+import packaging.database
+
+from os.path import join
+from operator import getitem, setitem, delitem
+from packaging.command.build import build
+from packaging.tests import unittest
+from packaging.tests.support import (TempdirManager, EnvironRestorer,
+                                     LoggingCatcher)
+from packaging.command.test import test
+from packaging.command import set_command
+from packaging.dist import Distribution
+
+
+EXPECTED_OUTPUT_RE = r'''FAIL: test_blah \(myowntestmodule.SomeTest\)
+----------------------------------------------------------------------
+Traceback \(most recent call last\):
+  File ".+/myowntestmodule.py", line \d+, in test_blah
+    self.fail\("horribly"\)
+AssertionError: horribly
+'''
+
+here = os.path.dirname(os.path.abspath(__file__))
+
+
+class MockBuildCmd(build):
+    build_lib = "mock build lib"
+    command_name = 'build'
+    plat_name = 'whatever'
+
+    def initialize_options(self):
+        pass
+
+    def finalize_options(self):
+        pass
+
+    def run(self):
+        self._record.append("build has run")
+
+
+class TestTest(TempdirManager,
+               EnvironRestorer,
+               LoggingCatcher,
+               unittest.TestCase):
+
+    restore_environ = ['PYTHONPATH']
+
+    def setUp(self):
+        super(TestTest, self).setUp()
+        self.addCleanup(packaging.database.clear_cache)
+        new_pythonpath = os.path.dirname(os.path.dirname(here))
+        pythonpath = os.environ.get('PYTHONPATH')
+        if pythonpath is not None:
+            new_pythonpath = os.pathsep.join((new_pythonpath, pythonpath))
+        os.environ['PYTHONPATH'] = new_pythonpath
+
+    def assert_re_match(self, pattern, string):
+        def quote(s):
+            lines = ['## ' + line for line in s.split('\n')]
+            sep = ["#" * 60]
+            return [''] + sep + lines + sep
+        msg = quote(pattern) + ["didn't match"] + quote(string)
+        msg = "\n".join(msg)
+        if not re.search(pattern, string):
+            self.fail(msg)
+
+    def prepare_dist(self, dist_name):
+        pkg_dir = join(os.path.dirname(__file__), "dists", dist_name)
+        temp_pkg_dir = join(self.mkdtemp(), dist_name)
+        shutil.copytree(pkg_dir, temp_pkg_dir)
+        return temp_pkg_dir
+
+    def safely_replace(self, obj, attr,
+                       new_val=None, delete=False, dictionary=False):
+        """Replace a object's attribute returning to its original state at the
+        end of the test run. Creates the attribute if not present before
+        (deleting afterwards). When delete=True, makes sure the value is del'd
+        for the test run. If dictionary is set to True, operates of its items
+        rather than attributes."""
+        if dictionary:
+            _setattr, _getattr, _delattr = setitem, getitem, delitem
+
+            def _hasattr(_dict, value):
+                return value in _dict
+        else:
+            _setattr, _getattr, _delattr, _hasattr = (setattr, getattr,
+                                                      delattr, hasattr)
+
+        orig_has_attr = _hasattr(obj, attr)
+        if orig_has_attr:
+            orig_val = _getattr(obj, attr)
+
+        if delete is False:
+            _setattr(obj, attr, new_val)
+        elif orig_has_attr:
+            _delattr(obj, attr)
+
+        def do_cleanup():
+            if orig_has_attr:
+                _setattr(obj, attr, orig_val)
+            elif _hasattr(obj, attr):
+                _delattr(obj, attr)
+
+        self.addCleanup(do_cleanup)
+
+    def test_runs_unittest(self):
+        module_name, a_module = self.prepare_a_module()
+        record = []
+        a_module.recorder = lambda *args: record.append("suite")
+
+        class MockTextTestRunner:
+            def __init__(*_, **__):
+                pass
+
+            def run(_self, suite):
+                record.append("run")
+
+        self.safely_replace(ut1, "TextTestRunner", MockTextTestRunner)
+
+        dist = Distribution()
+        cmd = test(dist)
+        cmd.suite = "%s.recorder" % module_name
+        cmd.run()
+        self.assertEqual(record, ["suite", "run"])
+
+    def test_builds_before_running_tests(self):
+        self.addCleanup(set_command, 'packaging.command.build.build')
+        set_command('packaging.tests.test_command_test.MockBuildCmd')
+
+        dist = Distribution()
+        dist.get_command_obj('build')._record = record = []
+        cmd = test(dist)
+        cmd.runner = self.prepare_named_function(lambda: None)
+        cmd.ensure_finalized()
+        cmd.run()
+        self.assertEqual(['build has run'], record)
+
+    def _test_works_with_2to3(self):
+        pass
+
+    def test_checks_requires(self):
+        dist = Distribution()
+        cmd = test(dist)
+        phony_project = 'ohno_ohno-impossible_1234-name_stop-that!'
+        cmd.tests_require = [phony_project]
+        cmd.ensure_finalized()
+        logs = self.get_logs(logging.WARNING)
+        self.assertEqual(1, len(logs))
+        self.assertIn(phony_project, logs[0])
+
+    def prepare_a_module(self):
+        tmp_dir = self.mkdtemp()
+        sys.path.append(tmp_dir)
+        self.addCleanup(sys.path.remove, tmp_dir)
+
+        self.write_file((tmp_dir, 'packaging_tests_a.py'), '')
+        import packaging_tests_a as a_module
+        return "packaging_tests_a", a_module
+
+    def prepare_named_function(self, func):
+        module_name, a_module = self.prepare_a_module()
+        a_module.recorder = func
+        return "%s.recorder" % module_name
+
+    def test_custom_runner(self):
+        dist = Distribution()
+        cmd = test(dist)
+        record = []
+        cmd.runner = self.prepare_named_function(
+            lambda: record.append("runner called"))
+        cmd.ensure_finalized()
+        cmd.run()
+        self.assertEqual(["runner called"], record)
+
+    def prepare_mock_ut2(self):
+        class MockUTClass:
+            def __init__(*_, **__):
+                pass
+
+            def discover(self):
+                pass
+
+            def run(self, _):
+                pass
+
+        class MockUTModule:
+            TestLoader = MockUTClass
+            TextTestRunner = MockUTClass
+
+        mock_ut2 = MockUTModule()
+        self.safely_replace(sys.modules, "unittest2",
+                            mock_ut2, dictionary=True)
+        return mock_ut2
+
+    def test_gets_unittest_discovery(self):
+        mock_ut2 = self.prepare_mock_ut2()
+        dist = Distribution()
+        cmd = test(dist)
+        self.safely_replace(ut1.TestLoader, "discover", lambda: None)
+        self.assertEqual(cmd.get_ut_with_discovery(), ut1)
+
+        del ut1.TestLoader.discover
+        self.assertEqual(cmd.get_ut_with_discovery(), mock_ut2)
+
+    def test_calls_discover(self):
+        self.safely_replace(ut1.TestLoader, "discover", delete=True)
+        mock_ut2 = self.prepare_mock_ut2()
+        record = []
+        mock_ut2.TestLoader.discover = lambda self, path: record.append(path)
+        dist = Distribution()
+        cmd = test(dist)
+        cmd.run()
+        self.assertEqual([os.curdir], record)
+
+
+def test_suite():
+    return unittest.makeSuite(TestTest)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_upload.py b/Lib/packaging/tests/test_command_upload.py
new file mode 100644
index 0000000..f2e338b
--- /dev/null
+++ b/Lib/packaging/tests/test_command_upload.py
@@ -0,0 +1,157 @@
+"""Tests for packaging.command.upload."""
+import os
+import sys
+
+from packaging.command.upload import upload
+from packaging.dist import Distribution
+from packaging.errors import PackagingOptionError
+
+from packaging.tests import unittest, support
+from packaging.tests.pypi_server import PyPIServer, PyPIServerTestCase
+
+
+PYPIRC_NOPASSWORD = """\
+[distutils]
+
+index-servers =
+    server1
+
+[server1]
+username:me
+"""
+
+PYPIRC = """\
+[distutils]
+
+index-servers =
+    server1
+    server2
+
+[server1]
+username:me
+password:secret
+
+[server2]
+username:meagain
+password: secret
+realm:acme
+repository:http://another.pypi/
+"""
+
+
+class UploadTestCase(support.TempdirManager, support.EnvironRestorer,
+                     support.LoggingCatcher, PyPIServerTestCase):
+
+    restore_environ = ['HOME']
+
+    def setUp(self):
+        super(UploadTestCase, self).setUp()
+        self.tmp_dir = self.mkdtemp()
+        self.rc = os.path.join(self.tmp_dir, '.pypirc')
+        os.environ['HOME'] = self.tmp_dir
+
+    def test_finalize_options(self):
+        # new format
+        self.write_file(self.rc, PYPIRC)
+        dist = Distribution()
+        cmd = upload(dist)
+        cmd.finalize_options()
+        for attr, expected in (('username', 'me'), ('password', 'secret'),
+                               ('realm', 'pypi'),
+                               ('repository', 'http://pypi.python.org/pypi')):
+            self.assertEqual(getattr(cmd, attr), expected)
+
+    def test_finalize_options_unsigned_identity_raises_exception(self):
+        self.write_file(self.rc, PYPIRC)
+        dist = Distribution()
+        cmd = upload(dist)
+        cmd.identity = True
+        cmd.sign = False
+        self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+    def test_saved_password(self):
+        # file with no password
+        self.write_file(self.rc, PYPIRC_NOPASSWORD)
+
+        # make sure it passes
+        dist = Distribution()
+        cmd = upload(dist)
+        cmd.ensure_finalized()
+        self.assertEqual(cmd.password, None)
+
+        # make sure we get it as well, if another command
+        # initialized it at the dist level
+        dist.password = 'xxx'
+        cmd = upload(dist)
+        cmd.finalize_options()
+        self.assertEqual(cmd.password, 'xxx')
+
+    def test_upload_without_files_raises_exception(self):
+        dist = Distribution()
+        cmd = upload(dist)
+        self.assertRaises(PackagingOptionError, cmd.run)
+
+    def test_upload(self):
+        path = os.path.join(self.tmp_dir, 'xxx')
+        self.write_file(path)
+        command, pyversion, filename = 'xxx', '3.3', path
+        dist_files = [(command, pyversion, filename)]
+
+        # lets run it
+        pkg_dir, dist = self.create_dist(dist_files=dist_files, author='dédé')
+        cmd = upload(dist)
+        cmd.ensure_finalized()
+        cmd.repository = self.pypi.full_address
+        cmd.run()
+
+        # what did we send ?
+        handler, request_data = self.pypi.requests[-1]
+        headers = handler.headers
+        #self.assertIn('dédé', str(request_data))
+        self.assertIn(b'xxx', request_data)
+
+        self.assertEqual(int(headers['content-length']), len(request_data))
+        self.assertLess(int(headers['content-length']), 2500)
+        self.assertTrue(headers['content-type'].startswith('multipart/form-data'))
+        self.assertEqual(handler.command, 'POST')
+        self.assertNotIn('\n', headers['authorization'])
+
+    def test_upload_docs(self):
+        path = os.path.join(self.tmp_dir, 'xxx')
+        self.write_file(path)
+        command, pyversion, filename = 'xxx', '3.3', path
+        dist_files = [(command, pyversion, filename)]
+        docs_path = os.path.join(self.tmp_dir, "build", "docs")
+        os.makedirs(docs_path)
+        self.write_file(os.path.join(docs_path, "index.html"), "yellow")
+        self.write_file(self.rc, PYPIRC)
+
+        # lets run it
+        pkg_dir, dist = self.create_dist(dist_files=dist_files, author='dédé')
+
+        cmd = upload(dist)
+        cmd.get_finalized_command("build").run()
+        cmd.upload_docs = True
+        cmd.ensure_finalized()
+        cmd.repository = self.pypi.full_address
+        try:
+            prev_dir = os.getcwd()
+            os.chdir(self.tmp_dir)
+            cmd.run()
+        finally:
+            os.chdir(prev_dir)
+
+        handler, request_data = self.pypi.requests[-1]
+        action, name, content = request_data.split(
+            "----------------GHSKFJDLGDS7543FJKLFHRE75642756743254"
+            .encode())[1:4]
+
+        self.assertIn(b'name=":action"', action)
+        self.assertIn(b'doc_upload', action)
+
+
+def test_suite():
+    return unittest.makeSuite(UploadTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_upload_docs.py b/Lib/packaging/tests/test_command_upload_docs.py
new file mode 100644
index 0000000..b103894
--- /dev/null
+++ b/Lib/packaging/tests/test_command_upload_docs.py
@@ -0,0 +1,205 @@
+"""Tests for packaging.command.upload_docs."""
+import os
+import sys
+import shutil
+import zipfile
+
+from packaging.command import upload_docs as upload_docs_mod
+from packaging.command.upload_docs import (upload_docs, zip_dir,
+                                           encode_multipart)
+from packaging.dist import Distribution
+from packaging.errors import PackagingFileError, PackagingOptionError
+
+from packaging.tests import unittest, support
+from packaging.tests.pypi_server import PyPIServerTestCase
+
+
+EXPECTED_MULTIPART_OUTPUT = [
+    b'---x',
+    b'Content-Disposition: form-data; name="username"',
+    b'',
+    b'wok',
+    b'---x',
+    b'Content-Disposition: form-data; name="password"',
+    b'',
+    b'secret',
+    b'---x',
+    b'Content-Disposition: form-data; name="picture"; filename="wok.png"',
+    b'',
+    b'PNG89',
+    b'---x--',
+    b'',
+]
+
+PYPIRC = """\
+[distutils]
+index-servers = server1
+
+[server1]
+repository = %s
+username = real_slim_shady
+password = long_island
+"""
+
+class UploadDocsTestCase(support.TempdirManager,
+                         support.EnvironRestorer,
+                         support.LoggingCatcher,
+                         PyPIServerTestCase):
+
+    restore_environ = ['HOME']
+
+    def setUp(self):
+        super(UploadDocsTestCase, self).setUp()
+        self.tmp_dir = self.mkdtemp()
+        self.rc = os.path.join(self.tmp_dir, '.pypirc')
+        os.environ['HOME'] = self.tmp_dir
+        self.dist = Distribution()
+        self.dist.metadata['Name'] = "distr-name"
+        self.cmd = upload_docs(self.dist)
+
+    def test_default_uploaddir(self):
+        sandbox = self.mkdtemp()
+        previous = os.getcwd()
+        os.chdir(sandbox)
+        try:
+            os.mkdir("build")
+            self.prepare_sample_dir("build")
+            self.cmd.ensure_finalized()
+            self.assertEqual(self.cmd.upload_dir, os.path.join("build", "docs"))
+        finally:
+            os.chdir(previous)
+
+    def test_default_uploaddir_looks_for_doc_also(self):
+        sandbox = self.mkdtemp()
+        previous = os.getcwd()
+        os.chdir(sandbox)
+        try:
+            os.mkdir("build")
+            self.prepare_sample_dir("build")
+            os.rename(os.path.join("build", "docs"), os.path.join("build", "doc"))
+            self.cmd.ensure_finalized()
+            self.assertEqual(self.cmd.upload_dir, os.path.join("build", "doc"))
+        finally:
+            os.chdir(previous)
+
+    def prepare_sample_dir(self, sample_dir=None):
+        if sample_dir is None:
+            sample_dir = self.mkdtemp()
+        os.mkdir(os.path.join(sample_dir, "docs"))
+        self.write_file(os.path.join(sample_dir, "docs", "index.html"), "Ce mortel ennui")
+        self.write_file(os.path.join(sample_dir, "index.html"), "Oh la la")
+        return sample_dir
+
+    def test_zip_dir(self):
+        source_dir = self.prepare_sample_dir()
+        compressed = zip_dir(source_dir)
+
+        zip_f = zipfile.ZipFile(compressed)
+        self.assertEqual(zip_f.namelist(), ['index.html', 'docs/index.html'])
+
+    def test_encode_multipart(self):
+        fields = [('username', 'wok'), ('password', 'secret')]
+        files = [('picture', 'wok.png', b'PNG89')]
+        content_type, body = encode_multipart(fields, files, b'-x')
+        self.assertEqual(b'multipart/form-data; boundary=-x', content_type)
+        self.assertEqual(EXPECTED_MULTIPART_OUTPUT, body.split(b'\r\n'))
+
+    def prepare_command(self):
+        self.cmd.upload_dir = self.prepare_sample_dir()
+        self.cmd.ensure_finalized()
+        self.cmd.repository = self.pypi.full_address
+        self.cmd.username = "username"
+        self.cmd.password = "password"
+
+    def test_upload(self):
+        self.prepare_command()
+        self.cmd.run()
+
+        self.assertEqual(len(self.pypi.requests), 1)
+        handler, request_data = self.pypi.requests[-1]
+        self.assertIn(b"content", request_data)
+        self.assertIn("Basic", handler.headers['authorization'])
+        self.assertTrue(handler.headers['content-type']
+            .startswith('multipart/form-data;'))
+
+        action, name, version, content =\
+            request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254".encode())[1:5]
+
+
+        # check that we picked the right chunks
+        self.assertIn(b'name=":action"', action)
+        self.assertIn(b'name="name"', name)
+        self.assertIn(b'name="version"', version)
+        self.assertIn(b'name="content"', content)
+
+        # check their contents
+        self.assertIn(b'doc_upload', action)
+        self.assertIn(b'distr-name', name)
+        self.assertIn(b'docs/index.html', content)
+        self.assertIn(b'Ce mortel ennui', content)
+
+    def test_https_connection(self):
+        https_called = False
+
+        orig_https = upload_docs_mod.http.client.HTTPConnection
+
+        def https_conn_wrapper(*args):
+            nonlocal https_called
+            https_called = True
+            # the testing server is http
+            return upload_docs_mod.http.client.HTTPConnection(*args)
+
+        upload_docs_mod.http.client.HTTPSConnection = https_conn_wrapper
+        try:
+            self.prepare_command()
+            self.cmd.run()
+            self.assertFalse(https_called)
+
+            self.cmd.repository = self.cmd.repository.replace("http", "https")
+            self.cmd.run()
+            self.assertTrue(https_called)
+        finally:
+            upload_docs_mod.http.client.HTTPConnection = orig_https
+
+    def test_handling_response(self):
+        self.pypi.default_response_status = '403 Forbidden'
+        self.prepare_command()
+        self.cmd.run()
+        self.assertIn('Upload failed (403): Forbidden', self.get_logs()[-1])
+
+        self.pypi.default_response_status = '301 Moved Permanently'
+        self.pypi.default_response_headers.append(("Location", "brand_new_location"))
+        self.cmd.run()
+        self.assertIn('brand_new_location', self.get_logs()[-1])
+
+    def test_reads_pypirc_data(self):
+        self.write_file(self.rc, PYPIRC % self.pypi.full_address)
+        self.cmd.repository = self.pypi.full_address
+        self.cmd.upload_dir = self.prepare_sample_dir()
+        self.cmd.ensure_finalized()
+        self.assertEqual(self.cmd.username, "real_slim_shady")
+        self.assertEqual(self.cmd.password, "long_island")
+
+    def test_checks_index_html_presence(self):
+        self.cmd.upload_dir = self.prepare_sample_dir()
+        os.remove(os.path.join(self.cmd.upload_dir, "index.html"))
+        self.assertRaises(PackagingFileError, self.cmd.ensure_finalized)
+
+    def test_checks_upload_dir(self):
+        self.cmd.upload_dir = self.prepare_sample_dir()
+        shutil.rmtree(os.path.join(self.cmd.upload_dir))
+        self.assertRaises(PackagingOptionError, self.cmd.ensure_finalized)
+
+    def test_show_response(self):
+        self.prepare_command()
+        self.cmd.show_response = True
+        self.cmd.run()
+        record = self.get_logs()[-1]
+        self.assertTrue(record, "should report the response")
+        self.assertIn(self.pypi.default_response_data, record)
+
+def test_suite():
+    return unittest.makeSuite(UploadDocsTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_compiler.py b/Lib/packaging/tests/test_compiler.py
new file mode 100644
index 0000000..2c620cb
--- /dev/null
+++ b/Lib/packaging/tests/test_compiler.py
@@ -0,0 +1,66 @@
+"""Tests for distutils.compiler."""
+import os
+
+from packaging.compiler import (get_default_compiler, customize_compiler,
+                                gen_lib_options)
+from packaging.tests import unittest, support
+
+
+class FakeCompiler:
+
+    name = 'fake'
+    description = 'Fake'
+
+    def library_dir_option(self, dir):
+        return "-L" + dir
+
+    def runtime_library_dir_option(self, dir):
+        return ["-cool", "-R" + dir]
+
+    def find_library_file(self, dirs, lib, debug=False):
+        return 'found'
+
+    def library_option(self, lib):
+        return "-l" + lib
+
+
+class CompilerTestCase(support.EnvironRestorer, unittest.TestCase):
+
+    restore_environ = ['AR', 'ARFLAGS']
+
+    @unittest.skipUnless(get_default_compiler() == 'unix',
+                        'irrelevant if default compiler is not unix')
+    def test_customize_compiler(self):
+
+        os.environ['AR'] = 'my_ar'
+        os.environ['ARFLAGS'] = '-arflags'
+
+        # make sure AR gets caught
+        class compiler:
+            name = 'unix'
+
+            def set_executables(self, **kw):
+                self.exes = kw
+
+        comp = compiler()
+        customize_compiler(comp)
+        self.assertEqual(comp.exes['archiver'], 'my_ar -arflags')
+
+    def test_gen_lib_options(self):
+        compiler = FakeCompiler()
+        libdirs = ['lib1', 'lib2']
+        runlibdirs = ['runlib1']
+        libs = [os.path.join('dir', 'name'), 'name2']
+
+        opts = gen_lib_options(compiler, libdirs, runlibdirs, libs)
+        wanted = ['-Llib1', '-Llib2', '-cool', '-Rrunlib1', 'found',
+                  '-lname2']
+        self.assertEqual(opts, wanted)
+
+
+def test_suite():
+    return unittest.makeSuite(CompilerTestCase)
+
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_config.py b/Lib/packaging/tests/test_config.py
new file mode 100644
index 0000000..8908c4f
--- /dev/null
+++ b/Lib/packaging/tests/test_config.py
@@ -0,0 +1,424 @@
+"""Tests for packaging.config."""
+import os
+import sys
+import logging
+from io import StringIO
+
+from packaging import command
+from packaging.dist import Distribution
+from packaging.errors import PackagingFileError
+from packaging.compiler import new_compiler, _COMPILERS
+from packaging.command.sdist import sdist
+
+from packaging.tests import unittest, support
+
+
+SETUP_CFG = """
+[metadata]
+name = RestingParrot
+version = 0.6.4
+author = Carl Meyer
+author_email = carl@oddbird.net
+maintainer = Éric Araujo
+maintainer_email = merwok@netwok.org
+summary = A sample project demonstrating packaging
+description-file = %(description-file)s
+keywords = packaging, sample project
+
+classifier =
+  Development Status :: 4 - Beta
+  Environment :: Console (Text Based)
+  Environment :: X11 Applications :: GTK; python_version < '3'
+  License :: OSI Approved :: MIT License
+  Programming Language :: Python
+  Programming Language :: Python :: 2
+  Programming Language :: Python :: 3
+
+requires_python = >=2.4, <3.2
+
+requires_dist =
+  PetShoppe
+  MichaelPalin (> 1.1)
+  pywin32; sys.platform == 'win32'
+  pysqlite2; python_version < '2.5'
+  inotify (0.0.1); sys.platform == 'linux2'
+
+requires_external = libxml2
+
+provides_dist = packaging-sample-project (0.2)
+                unittest2-sample-project
+
+project_url =
+  Main repository, http://bitbucket.org/carljm/sample-distutils2-project
+  Fork in progress, http://bitbucket.org/Merwok/sample-distutils2-project
+
+[files]
+packages_root = src
+
+packages = one
+           two
+           three
+
+modules = haven
+
+scripts =
+  script1.py
+  scripts/find-coconuts
+  bin/taunt
+
+package_data =
+  cheese = data/templates/*
+
+extra_files = %(extra-files)s
+
+# Replaces MANIFEST.in
+sdist_extra =
+  include THANKS HACKING
+  recursive-include examples *.txt *.py
+  prune examples/sample?/build
+
+resources=
+  bm/ {b1,b2}.gif = {icon}
+  Cf*/ *.CFG = {config}/baBar/
+  init_script = {script}/JunGle/
+
+[global]
+commands =
+    packaging.tests.test_config.FooBarBazTest
+
+compilers =
+    packaging.tests.test_config.DCompiler
+
+setup_hook = %(setup-hook)s
+
+
+
+[install_dist]
+sub_commands = foo
+"""
+
+# Can not be merged with SETUP_CFG else install_dist
+# command will fail when trying to compile C sources
+EXT_SETUP_CFG = """
+[files]
+packages = one
+           two
+
+[extension=speed_coconuts]
+name = one.speed_coconuts
+sources = c_src/speed_coconuts.c
+extra_link_args = "`gcc -print-file-name=libgcc.a`" -shared
+define_macros = HAVE_CAIRO HAVE_GTK2
+libraries = gecodeint gecodekernel -- sys.platform != 'win32'
+    GecodeInt GecodeKernel -- sys.platform == 'win32'
+
+[extension=fast_taunt]
+name = three.fast_taunt
+sources = cxx_src/utils_taunt.cxx
+          cxx_src/python_module.cxx
+include_dirs = /usr/include/gecode
+    /usr/include/blitz
+extra_compile_args = -fPIC -O2
+    -DGECODE_VERSION=$(./gecode_version) -- sys.platform != 'win32'
+    /DGECODE_VERSION='win32' -- sys.platform == 'win32'
+language = cxx
+
+"""
+
+
+class DCompiler:
+    name = 'd'
+    description = 'D Compiler'
+
+    def __init__(self, *args):
+        pass
+
+
+def hook(content):
+    content['metadata']['version'] += '.dev1'
+
+
+class FooBarBazTest:
+
+    def __init__(self, dist):
+        self.distribution = dist
+
+    @classmethod
+    def get_command_name(cls):
+        return 'foo'
+
+    def run(self):
+        self.distribution.foo_was_here = True
+
+    def nothing(self):
+        pass
+
+    def get_source_files(self):
+        return []
+
+    ensure_finalized = finalize_options = initialize_options = nothing
+
+
+class ConfigTestCase(support.TempdirManager,
+                     support.EnvironRestorer,
+                     support.LoggingCatcher,
+                     unittest.TestCase):
+
+    restore_environ = ['PLAT']
+
+    def setUp(self):
+        super(ConfigTestCase, self).setUp()
+        self.addCleanup(setattr, sys, 'stdout', sys.stdout)
+        self.addCleanup(setattr, sys, 'stderr', sys.stderr)
+        sys.stdout = StringIO()
+        sys.stderr = StringIO()
+
+        self.addCleanup(os.chdir, os.getcwd())
+        tempdir = self.mkdtemp()
+        os.chdir(tempdir)
+        self.tempdir = tempdir
+
+    def write_setup(self, kwargs=None):
+        opts = {'description-file': 'README', 'extra-files': '',
+                'setup-hook': 'packaging.tests.test_config.hook'}
+        if kwargs:
+            opts.update(kwargs)
+        self.write_file('setup.cfg', SETUP_CFG % opts)
+
+    def get_dist(self):
+        dist = Distribution()
+        dist.parse_config_files()
+        return dist
+
+    def test_config(self):
+        self.write_setup()
+        self.write_file('README', 'yeah')
+        os.mkdir('bm')
+        self.write_file(('bm', 'b1.gif'), '')
+        self.write_file(('bm', 'b2.gif'), '')
+        os.mkdir('Cfg')
+        self.write_file(('Cfg', 'data.CFG'), '')
+        self.write_file('init_script', '')
+
+        # try to load the metadata now
+        dist = self.get_dist()
+
+        # check what was done
+        self.assertEqual(dist.metadata['Author'], 'Carl Meyer')
+        self.assertEqual(dist.metadata['Author-Email'], 'carl@oddbird.net')
+
+        # the hook adds .dev1
+        self.assertEqual(dist.metadata['Version'], '0.6.4.dev1')
+
+        wanted = [
+            'Development Status :: 4 - Beta',
+            'Environment :: Console (Text Based)',
+            "Environment :: X11 Applications :: GTK; python_version < '3'",
+            'License :: OSI Approved :: MIT License',
+            'Programming Language :: Python',
+            'Programming Language :: Python :: 2',
+            'Programming Language :: Python :: 3']
+        self.assertEqual(dist.metadata['Classifier'], wanted)
+
+        wanted = ['packaging', 'sample project']
+        self.assertEqual(dist.metadata['Keywords'], wanted)
+
+        self.assertEqual(dist.metadata['Requires-Python'], '>=2.4, <3.2')
+
+        wanted = ['PetShoppe',
+                  'MichaelPalin (> 1.1)',
+                  "pywin32; sys.platform == 'win32'",
+                  "pysqlite2; python_version < '2.5'",
+                  "inotify (0.0.1); sys.platform == 'linux2'"]
+
+        self.assertEqual(dist.metadata['Requires-Dist'], wanted)
+        urls = [('Main repository',
+                 'http://bitbucket.org/carljm/sample-distutils2-project'),
+                ('Fork in progress',
+                 'http://bitbucket.org/Merwok/sample-distutils2-project')]
+        self.assertEqual(dist.metadata['Project-Url'], urls)
+
+        self.assertEqual(dist.packages, ['one', 'two', 'three'])
+        self.assertEqual(dist.py_modules, ['haven'])
+        self.assertEqual(dist.package_data, {'cheese': 'data/templates/*'})
+        self.assertEqual(
+            {'bm/b1.gif': '{icon}/b1.gif',
+             'bm/b2.gif': '{icon}/b2.gif',
+             'Cfg/data.CFG': '{config}/baBar/data.CFG',
+             'init_script': '{script}/JunGle/init_script'},
+             dist.data_files)
+
+        self.assertEqual(dist.package_dir, 'src')
+
+        # Make sure we get the foo command loaded.  We use a string comparison
+        # instead of assertIsInstance because the class is not the same when
+        # this test is run directly: foo is packaging.tests.test_config.Foo
+        # because get_command_class uses the full name, but a bare "Foo" in
+        # this file would be __main__.Foo when run as "python test_config.py".
+        # The name FooBarBazTest should be unique enough to prevent
+        # collisions.
+        self.assertEqual('FooBarBazTest',
+                         dist.get_command_obj('foo').__class__.__name__)
+
+        # did the README got loaded ?
+        self.assertEqual(dist.metadata['description'], 'yeah')
+
+        # do we have the D Compiler enabled ?
+        self.assertIn('d', _COMPILERS)
+        d = new_compiler(compiler='d')
+        self.assertEqual(d.description, 'D Compiler')
+
+    def test_multiple_description_file(self):
+        self.write_setup({'description-file': 'README  CHANGES'})
+        self.write_file('README', 'yeah')
+        self.write_file('CHANGES', 'changelog2')
+        dist = self.get_dist()
+        self.assertEqual(dist.metadata.requires_files, ['README', 'CHANGES'])
+
+    def test_multiline_description_file(self):
+        self.write_setup({'description-file': 'README\n  CHANGES'})
+        self.write_file('README', 'yeah')
+        self.write_file('CHANGES', 'changelog')
+        dist = self.get_dist()
+        self.assertEqual(dist.metadata['description'], 'yeah\nchangelog')
+        self.assertEqual(dist.metadata.requires_files, ['README', 'CHANGES'])
+
+    def test_parse_extensions_in_config(self):
+        self.write_file('setup.cfg', EXT_SETUP_CFG)
+        dist = self.get_dist()
+
+        ext_modules = dict((mod.name, mod) for mod in dist.ext_modules)
+        self.assertEqual(len(ext_modules), 2)
+        ext = ext_modules.get('one.speed_coconuts')
+        self.assertEqual(ext.sources, ['c_src/speed_coconuts.c'])
+        self.assertEqual(ext.define_macros, ['HAVE_CAIRO', 'HAVE_GTK2'])
+        libs = ['gecodeint', 'gecodekernel']
+        if sys.platform == 'win32':
+            libs = ['GecodeInt', 'GecodeKernel']
+        self.assertEqual(ext.libraries, libs)
+        self.assertEqual(ext.extra_link_args,
+            ['`gcc -print-file-name=libgcc.a`', '-shared'])
+
+        ext = ext_modules.get('three.fast_taunt')
+        self.assertEqual(ext.sources,
+            ['cxx_src/utils_taunt.cxx', 'cxx_src/python_module.cxx'])
+        self.assertEqual(ext.include_dirs,
+            ['/usr/include/gecode', '/usr/include/blitz'])
+        cargs = ['-fPIC', '-O2']
+        if sys.platform == 'win32':
+            cargs.append("/DGECODE_VERSION='win32'")
+        else:
+            cargs.append('-DGECODE_VERSION=$(./gecode_version)')
+        self.assertEqual(ext.extra_compile_args, cargs)
+        self.assertEqual(ext.language, 'cxx')
+
+    def test_missing_setuphook_warns(self):
+        self.write_setup({'setup-hook': 'this.does._not.exist'})
+        self.write_file('README', 'yeah')
+        dist = self.get_dist()
+        logs = self.get_logs(logging.WARNING)
+        self.assertEqual(1, len(logs))
+        self.assertIn('could not import setup_hook', logs[0])
+
+    def test_metadata_requires_description_files_missing(self):
+        self.write_setup({'description-file': 'README\n  README2'})
+        self.write_file('README', 'yeah')
+        self.write_file('README2', 'yeah')
+        os.mkdir('src')
+        self.write_file(('src', 'haven.py'), '#')
+        self.write_file('script1.py', '#')
+        os.mkdir('scripts')
+        self.write_file(('scripts', 'find-coconuts'), '#')
+        os.mkdir('bin')
+        self.write_file(('bin', 'taunt'), '#')
+
+        for pkg in ('one', 'two', 'three'):
+            pkg = os.path.join('src', pkg)
+            os.mkdir(pkg)
+            self.write_file((pkg, '__init__.py'), '#')
+
+        dist = self.get_dist()
+        cmd = sdist(dist)
+        cmd.finalize_options()
+        cmd.get_file_list()
+        self.assertRaises(PackagingFileError, cmd.make_distribution)
+
+    def test_metadata_requires_description_files(self):
+        # Create the following file structure:
+        #   README
+        #   README2
+        #   script1.py
+        #   scripts/
+        #       find-coconuts
+        #   bin/
+        #       taunt
+        #   src/
+        #       haven.py
+        #       one/__init__.py
+        #       two/__init__.py
+        #       three/__init__.py
+
+        self.write_setup({'description-file': 'README\n  README2',
+                          'extra-files': '\n  README3'})
+        self.write_file('README', 'yeah 1')
+        self.write_file('README2', 'yeah 2')
+        self.write_file('README3', 'yeah 3')
+        os.mkdir('src')
+        self.write_file(('src', 'haven.py'), '#')
+        self.write_file('script1.py', '#')
+        os.mkdir('scripts')
+        self.write_file(('scripts', 'find-coconuts'), '#')
+        os.mkdir('bin')
+        self.write_file(('bin', 'taunt'), '#')
+
+        for pkg in ('one', 'two', 'three'):
+            pkg = os.path.join('src', pkg)
+            os.mkdir(pkg)
+            self.write_file((pkg, '__init__.py'), '#')
+
+        dist = self.get_dist()
+        self.assertIn('yeah 1\nyeah 2', dist.metadata['description'])
+
+        cmd = sdist(dist)
+        cmd.finalize_options()
+        cmd.get_file_list()
+        self.assertRaises(PackagingFileError, cmd.make_distribution)
+
+        self.write_setup({'description-file': 'README\n  README2',
+                          'extra-files': '\n  README2\n    README'})
+        dist = self.get_dist()
+        cmd = sdist(dist)
+        cmd.finalize_options()
+        cmd.get_file_list()
+        cmd.make_distribution()
+        with open('MANIFEST') as fp:
+            self.assertIn('README\nREADME2\n', fp.read())
+
+    def test_sub_commands(self):
+        self.write_setup()
+        self.write_file('README', 'yeah')
+        os.mkdir('src')
+        self.write_file(('src', 'haven.py'), '#')
+        self.write_file('script1.py', '#')
+        os.mkdir('scripts')
+        self.write_file(('scripts', 'find-coconuts'), '#')
+        os.mkdir('bin')
+        self.write_file(('bin', 'taunt'), '#')
+
+        for pkg in ('one', 'two', 'three'):
+            pkg = os.path.join('src', pkg)
+            os.mkdir(pkg)
+            self.write_file((pkg, '__init__.py'), '#')
+
+        # try to run the install command to see if foo is called
+        dist = self.get_dist()
+        self.assertIn('foo', command.get_command_names())
+        self.assertEqual('FooBarBazTest',
+                         dist.get_command_obj('foo').__class__.__name__)
+
+
+def test_suite():
+    return unittest.makeSuite(ConfigTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_create.py b/Lib/packaging/tests/test_create.py
new file mode 100644
index 0000000..99ab063
--- /dev/null
+++ b/Lib/packaging/tests/test_create.py
@@ -0,0 +1,235 @@
+"""Tests for packaging.create."""
+import io
+import os
+import sys
+import sysconfig
+from textwrap import dedent
+from packaging.create import MainProgram, ask_yn, ask, main
+
+from packaging.tests import support, unittest
+
+
+class CreateTestCase(support.TempdirManager,
+                     support.EnvironRestorer,
+                     unittest.TestCase):
+
+    restore_environ = ['PLAT']
+
+    def setUp(self):
+        super(CreateTestCase, self).setUp()
+        self._stdin = sys.stdin  # TODO use Inputs
+        self._stdout = sys.stdout
+        sys.stdin = io.StringIO()
+        sys.stdout = io.StringIO()
+        self._cwd = os.getcwd()
+        self.wdir = self.mkdtemp()
+        os.chdir(self.wdir)
+        # patch sysconfig
+        self._old_get_paths = sysconfig.get_paths
+        sysconfig.get_paths = lambda *args, **kwargs: {
+            'man': sys.prefix + '/share/man',
+            'doc': sys.prefix + '/share/doc/pyxfoil', }
+
+    def tearDown(self):
+        super(CreateTestCase, self).tearDown()
+        sys.stdin = self._stdin
+        sys.stdout = self._stdout
+        os.chdir(self._cwd)
+        sysconfig.get_paths = self._old_get_paths
+
+    def test_ask_yn(self):
+        sys.stdin.write('y\n')
+        sys.stdin.seek(0)
+        self.assertEqual('y', ask_yn('is this a test'))
+
+    def test_ask(self):
+        sys.stdin.write('a\n')
+        sys.stdin.write('b\n')
+        sys.stdin.seek(0)
+        self.assertEqual('a', ask('is this a test'))
+        self.assertEqual('b', ask(str(list(range(0, 70))), default='c',
+                                  lengthy=True))
+
+    def test_set_multi(self):
+        mainprogram = MainProgram()
+        sys.stdin.write('aaaaa\n')
+        sys.stdin.seek(0)
+        mainprogram.data['author'] = []
+        mainprogram._set_multi('_set_multi test', 'author')
+        self.assertEqual(['aaaaa'], mainprogram.data['author'])
+
+    def test_find_files(self):
+        # making sure we scan a project dir correctly
+        mainprogram = MainProgram()
+
+        # building the structure
+        tempdir = self.wdir
+        dirs = ['pkg1', 'data', 'pkg2', 'pkg2/sub']
+        files = ['README', 'setup.cfg', 'foo.py',
+                 'pkg1/__init__.py', 'pkg1/bar.py',
+                 'data/data1', 'pkg2/__init__.py',
+                 'pkg2/sub/__init__.py']
+
+        for dir_ in dirs:
+            os.mkdir(os.path.join(tempdir, dir_))
+
+        for file_ in files:
+            path = os.path.join(tempdir, file_)
+            self.write_file(path, 'xxx')
+
+        mainprogram._find_files()
+        mainprogram.data['packages'].sort()
+
+        # do we have what we want?
+        self.assertEqual(mainprogram.data['packages'],
+                         ['pkg1', 'pkg2', 'pkg2.sub'])
+        self.assertEqual(mainprogram.data['modules'], ['foo'])
+        data_fn = os.path.join('data', 'data1')
+        self.assertEqual(set(mainprogram.data['extra_files']),
+                         set(['setup.cfg', 'README', data_fn]))
+
+    def test_convert_setup_py_to_cfg(self):
+        self.write_file((self.wdir, 'setup.py'),
+                        dedent("""
+        # -*- coding: utf-8 -*-
+        from distutils.core import setup
+
+        long_description = '''My super Death-scription
+        barbar is now on the public domain,
+        ho, baby !'''
+
+        setup(name='pyxfoil',
+              version='0.2',
+              description='Python bindings for the Xfoil engine',
+              long_description=long_description,
+              maintainer='André Espaze',
+              maintainer_email='andre.espaze@logilab.fr',
+              url='http://www.python-science.org/project/pyxfoil',
+              license='GPLv2',
+              packages=['pyxfoil', 'babar', 'me'],
+              data_files=[
+                  ('share/doc/pyxfoil', ['README.rst']),
+                  ('share/man', ['pyxfoil.1']),
+                         ],
+              py_modules=['my_lib', 'mymodule'],
+              package_dir={
+                  'babar': '',
+                  'me': 'Martinique/Lamentin',
+                          },
+              package_data={
+                  'babar': ['Pom', 'Flora', 'Alexander'],
+                  'me': ['dady', 'mumy', 'sys', 'bro'],
+                  '':  ['setup.py', 'README'],
+                  'pyxfoil': ['fengine.so'],
+                           },
+              scripts=['my_script', 'bin/run'],
+              )
+        """))
+        sys.stdin.write('y\n')
+        sys.stdin.seek(0)
+        main()
+
+        with open(os.path.join(self.wdir, 'setup.cfg')) as fp:
+            lines = set(line.rstrip() for line in fp)
+
+        # FIXME don't use sets
+        self.assertEqual(lines, set(['',
+            '[metadata]',
+            'version = 0.2',
+            'name = pyxfoil',
+            'maintainer = André Espaze',
+            'description = My super Death-scription',
+            '       |barbar is now on the public domain,',
+            '       |ho, baby !',
+            'maintainer_email = andre.espaze@logilab.fr',
+            'home_page = http://www.python-science.org/project/pyxfoil',
+            'download_url = UNKNOWN',
+            'summary = Python bindings for the Xfoil engine',
+            '[files]',
+            'modules = my_lib',
+            '    mymodule',
+            'packages = pyxfoil',
+            '    babar',
+            '    me',
+            'extra_files = Martinique/Lamentin/dady',
+            '    Martinique/Lamentin/mumy',
+            '    Martinique/Lamentin/sys',
+            '    Martinique/Lamentin/bro',
+            '    Pom',
+            '    Flora',
+            '    Alexander',
+            '    setup.py',
+            '    README',
+            '    pyxfoil/fengine.so',
+            'scripts = my_script',
+            '    bin/run',
+            'resources =',
+            '    README.rst = {doc}',
+            '    pyxfoil.1 = {man}',
+        ]))
+
+    def test_convert_setup_py_to_cfg_with_description_in_readme(self):
+        self.write_file((self.wdir, 'setup.py'),
+                        dedent("""
+        # -*- coding: utf-8 -*-
+        from distutils.core import setup
+        fp = open('README.txt')
+        try:
+            long_description = fp.read()
+        finally:
+            fp.close()
+
+        setup(name='pyxfoil',
+              version='0.2',
+              description='Python bindings for the Xfoil engine',
+              long_description=long_description,
+              maintainer='André Espaze',
+              maintainer_email='andre.espaze@logilab.fr',
+              url='http://www.python-science.org/project/pyxfoil',
+              license='GPLv2',
+              packages=['pyxfoil'],
+              package_data={'pyxfoil': ['fengine.so', 'babar.so']},
+              data_files=[
+                ('share/doc/pyxfoil', ['README.rst']),
+                ('share/man', ['pyxfoil.1']),
+              ],
+        )
+        """))
+        self.write_file((self.wdir, 'README.txt'),
+                        dedent('''
+My super Death-scription
+barbar is now in the public domain,
+ho, baby!
+                        '''))
+        sys.stdin.write('y\n')
+        sys.stdin.seek(0)
+        # FIXME Out of memory error.
+        main()
+        with open(os.path.join(self.wdir, 'setup.cfg')) as fp:
+            lines = set(line.rstrip() for line in fp)
+
+        self.assertEqual(lines, set(['',
+            '[metadata]',
+            'version = 0.2',
+            'name = pyxfoil',
+            'maintainer = André Espaze',
+            'maintainer_email = andre.espaze@logilab.fr',
+            'home_page = http://www.python-science.org/project/pyxfoil',
+            'download_url = UNKNOWN',
+            'summary = Python bindings for the Xfoil engine',
+            'description-file = README.txt',
+            '[files]',
+            'packages = pyxfoil',
+            'extra_files = pyxfoil/fengine.so',
+            '    pyxfoil/babar.so',
+            'resources =',
+            '    README.rst = {doc}',
+            '    pyxfoil.1 = {man}',
+        ]))
+
+
+def test_suite():
+    return unittest.makeSuite(CreateTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_cygwinccompiler.py b/Lib/packaging/tests/test_cygwinccompiler.py
new file mode 100644
index 0000000..17c43cd
--- /dev/null
+++ b/Lib/packaging/tests/test_cygwinccompiler.py
@@ -0,0 +1,88 @@
+"""Tests for packaging.cygwinccompiler."""
+import os
+import sys
+import sysconfig
+from packaging.compiler.cygwinccompiler import (
+    check_config_h, get_msvcr,
+    CONFIG_H_OK, CONFIG_H_NOTOK, CONFIG_H_UNCERTAIN)
+
+from packaging.tests import unittest, support
+
+
+class CygwinCCompilerTestCase(support.TempdirManager,
+                              unittest.TestCase):
+
+    def setUp(self):
+        super(CygwinCCompilerTestCase, self).setUp()
+        self.version = sys.version
+        self.python_h = os.path.join(self.mkdtemp(), 'python.h')
+        self.old_get_config_h_filename = sysconfig.get_config_h_filename
+        sysconfig.get_config_h_filename = self._get_config_h_filename
+
+    def tearDown(self):
+        sys.version = self.version
+        sysconfig.get_config_h_filename = self.old_get_config_h_filename
+        super(CygwinCCompilerTestCase, self).tearDown()
+
+    def _get_config_h_filename(self):
+        return self.python_h
+
+    def test_check_config_h(self):
+        # check_config_h looks for "GCC" in sys.version first
+        # returns CONFIG_H_OK if found
+        sys.version = ('2.6.1 (r261:67515, Dec  6 2008, 16:42:21) \n[GCC '
+                       '4.0.1 (Apple Computer, Inc. build 5370)]')
+
+        self.assertEqual(check_config_h()[0], CONFIG_H_OK)
+
+        # then it tries to see if it can find "__GNUC__" in pyconfig.h
+        sys.version = 'something without the *CC word'
+
+        # if the file doesn't exist it returns  CONFIG_H_UNCERTAIN
+        self.assertEqual(check_config_h()[0], CONFIG_H_UNCERTAIN)
+
+        # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK
+        self.write_file(self.python_h, 'xxx')
+        self.assertEqual(check_config_h()[0], CONFIG_H_NOTOK)
+
+        # and CONFIG_H_OK if __GNUC__ is found
+        self.write_file(self.python_h, 'xxx __GNUC__ xxx')
+        self.assertEqual(check_config_h()[0], CONFIG_H_OK)
+
+    def test_get_msvcr(self):
+        # none
+        sys.version = ('2.6.1 (r261:67515, Dec  6 2008, 16:42:21) '
+                       '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]')
+        self.assertEqual(get_msvcr(), None)
+
+        # MSVC 7.0
+        sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
+                       '[MSC v.1300 32 bits (Intel)]')
+        self.assertEqual(get_msvcr(), ['msvcr70'])
+
+        # MSVC 7.1
+        sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
+                       '[MSC v.1310 32 bits (Intel)]')
+        self.assertEqual(get_msvcr(), ['msvcr71'])
+
+        # VS2005 / MSVC 8.0
+        sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
+                       '[MSC v.1400 32 bits (Intel)]')
+        self.assertEqual(get_msvcr(), ['msvcr80'])
+
+        # VS2008 / MSVC 9.0
+        sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
+                       '[MSC v.1500 32 bits (Intel)]')
+        self.assertEqual(get_msvcr(), ['msvcr90'])
+
+        # unknown
+        sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
+                       '[MSC v.1999 32 bits (Intel)]')
+        self.assertRaises(ValueError, get_msvcr)
+
+
+def test_suite():
+    return unittest.makeSuite(CygwinCCompilerTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_database.py b/Lib/packaging/tests/test_database.py
new file mode 100644
index 0000000..c8d9415
--- /dev/null
+++ b/Lib/packaging/tests/test_database.py
@@ -0,0 +1,506 @@
+import os
+import io
+import csv
+import imp
+import sys
+import shutil
+import zipfile
+import tempfile
+from os.path import relpath  # separate import for backport concerns
+from hashlib import md5
+
+from packaging.errors import PackagingError
+from packaging.metadata import Metadata
+from packaging.tests import unittest, run_unittest, support, TESTFN
+
+from packaging.database import (
+    Distribution, EggInfoDistribution, get_distribution, get_distributions,
+    provides_distribution, obsoletes_distribution, get_file_users,
+    enable_cache, disable_cache, distinfo_dirname, _yield_distributions)
+
+# TODO Add a test for getting a distribution provided by another distribution
+# TODO Add a test for absolute pathed RECORD items (e.g. /etc/myapp/config.ini)
+# TODO Add tests from the former pep376 project (zipped site-packages, etc.)
+
+
+def get_hexdigest(filename):
+    with open(filename, 'rb') as file:
+        checksum = md5(file.read())
+    return checksum.hexdigest()
+
+
+def record_pieces(file):
+    path = relpath(file, sys.prefix)
+    digest = get_hexdigest(file)
+    size = os.path.getsize(file)
+    return [path, digest, size]
+
+
+class CommonDistributionTests:
+    """Mixin used to test the interface common to both Distribution classes.
+
+    Derived classes define cls, sample_dist, dirs and records.  These
+    attributes are used in test methods.  See source code for details.
+    """
+
+    def setUp(self):
+        super(CommonDistributionTests, self).setUp()
+        self.addCleanup(enable_cache)
+        disable_cache()
+        self.fake_dists_path = os.path.abspath(
+            os.path.join(os.path.dirname(__file__), 'fake_dists'))
+
+    def test_instantiation(self):
+        # check that useful attributes are here
+        name, version, distdir = self.sample_dist
+        here = os.path.abspath(os.path.dirname(__file__))
+        dist_path = os.path.join(here, 'fake_dists', distdir)
+
+        dist = self.dist = self.cls(dist_path)
+        self.assertEqual(dist.path, dist_path)
+        self.assertEqual(dist.name, name)
+        self.assertEqual(dist.metadata['Name'], name)
+        self.assertIsInstance(dist.metadata, Metadata)
+        self.assertEqual(dist.version, version)
+        self.assertEqual(dist.metadata['Version'], version)
+
+    def test_repr(self):
+        dist = self.cls(self.dirs[0])
+        # just check that the class name is in the repr
+        self.assertIn(self.cls.__name__, repr(dist))
+
+    def test_comparison(self):
+        # tests for __eq__ and __hash__
+        dist = self.cls(self.dirs[0])
+        dist2 = self.cls(self.dirs[0])
+        dist3 = self.cls(self.dirs[1])
+        self.assertIn(dist, {dist: True})
+        self.assertEqual(dist, dist)
+
+        self.assertIsNot(dist, dist2)
+        self.assertEqual(dist, dist2)
+        self.assertNotEqual(dist, dist3)
+        self.assertNotEqual(dist, ())
+
+    def test_list_installed_files(self):
+        for dir_ in self.dirs:
+            dist = self.cls(dir_)
+            for path, md5_, size in dist.list_installed_files():
+                record_data = self.records[dist.path]
+                self.assertIn(path, record_data)
+                self.assertEqual(md5_, record_data[path][0])
+                self.assertEqual(size, record_data[path][1])
+
+
+class TestDistribution(CommonDistributionTests, unittest.TestCase):
+
+    cls = Distribution
+    sample_dist = 'choxie', '2.0.0.9', 'choxie-2.0.0.9.dist-info'
+
+    def setUp(self):
+        super(TestDistribution, self).setUp()
+        self.dirs = [os.path.join(self.fake_dists_path, f)
+                     for f in os.listdir(self.fake_dists_path)
+                     if f.endswith('.dist-info')]
+
+        self.records = {}
+        for distinfo_dir in self.dirs:
+            record_file = os.path.join(distinfo_dir, 'RECORD')
+            with open(record_file, 'w') as file:
+                record_writer = csv.writer(
+                    file, delimiter=',', quoting=csv.QUOTE_NONE)
+
+                dist_location = distinfo_dir.replace('.dist-info', '')
+
+                for path, dirs, files in os.walk(dist_location):
+                    for f in files:
+                        record_writer.writerow(record_pieces(
+                                               os.path.join(path, f)))
+                for file in ('INSTALLER', 'METADATA', 'REQUESTED'):
+                    record_writer.writerow(record_pieces(
+                                           os.path.join(distinfo_dir, file)))
+                record_writer.writerow([relpath(record_file, sys.prefix)])
+
+            with open(record_file) as file:
+                record_reader = csv.reader(file)
+                record_data = {}
+                for row in record_reader:
+                    path, md5_, size = (row[:] +
+                                        [None for i in range(len(row), 3)])
+                    record_data[path] = md5_, size
+            self.records[distinfo_dir] = record_data
+
+    def tearDown(self):
+        for distinfo_dir in self.dirs:
+            record_file = os.path.join(distinfo_dir, 'RECORD')
+            open(record_file, 'w').close()
+        super(TestDistribution, self).tearDown()
+
+    def test_instantiation(self):
+        super(TestDistribution, self).test_instantiation()
+        self.assertIsInstance(self.dist.requested, bool)
+
+    def test_uses(self):
+        # Test to determine if a distribution uses a specified file.
+        # Criteria to test against
+        distinfo_name = 'grammar-1.0a4'
+        distinfo_dir = os.path.join(self.fake_dists_path,
+                                    distinfo_name + '.dist-info')
+        true_path = [self.fake_dists_path, distinfo_name,
+                     'grammar', 'utils.py']
+        true_path = relpath(os.path.join(*true_path), sys.prefix)
+        false_path = [self.fake_dists_path, 'towel_stuff-0.1', 'towel_stuff',
+                      '__init__.py']
+        false_path = relpath(os.path.join(*false_path), sys.prefix)
+
+        # Test if the distribution uses the file in question
+        dist = Distribution(distinfo_dir)
+        self.assertTrue(dist.uses(true_path))
+        self.assertFalse(dist.uses(false_path))
+
+    def test_get_distinfo_file(self):
+        # Test the retrieval of dist-info file objects.
+        distinfo_name = 'choxie-2.0.0.9'
+        other_distinfo_name = 'grammar-1.0a4'
+        distinfo_dir = os.path.join(self.fake_dists_path,
+                                    distinfo_name + '.dist-info')
+        dist = Distribution(distinfo_dir)
+        # Test for known good file matches
+        distinfo_files = [
+            # Relative paths
+            'INSTALLER', 'METADATA',
+            # Absolute paths
+            os.path.join(distinfo_dir, 'RECORD'),
+            os.path.join(distinfo_dir, 'REQUESTED'),
+        ]
+
+        for distfile in distinfo_files:
+            with dist.get_distinfo_file(distfile) as value:
+                self.assertIsInstance(value, io.TextIOWrapper)
+                # Is it the correct file?
+                self.assertEqual(value.name,
+                                 os.path.join(distinfo_dir, distfile))
+
+        # Test an absolute path that is part of another distributions dist-info
+        other_distinfo_file = os.path.join(
+            self.fake_dists_path, other_distinfo_name + '.dist-info',
+            'REQUESTED')
+        self.assertRaises(PackagingError, dist.get_distinfo_file,
+                          other_distinfo_file)
+        # Test for a file that should not exist
+        self.assertRaises(PackagingError, dist.get_distinfo_file,
+                          'MAGICFILE')
+
+    def test_list_distinfo_files(self):
+        # Test for the iteration of RECORD path entries.
+        distinfo_name = 'towel_stuff-0.1'
+        distinfo_dir = os.path.join(self.fake_dists_path,
+                                    distinfo_name + '.dist-info')
+        dist = Distribution(distinfo_dir)
+        # Test for the iteration of the raw path
+        distinfo_record_paths = self.records[distinfo_dir].keys()
+        found = dist.list_distinfo_files()
+        self.assertEqual(sorted(found), sorted(distinfo_record_paths))
+        # Test for the iteration of local absolute paths
+        distinfo_record_paths = [os.path.join(sys.prefix, path)
+            for path in self.records[distinfo_dir]]
+        found = dist.list_distinfo_files(local=True)
+        self.assertEqual(sorted(found), sorted(distinfo_record_paths))
+
+    def test_get_resources_path(self):
+        distinfo_name = 'babar-0.1'
+        distinfo_dir = os.path.join(self.fake_dists_path,
+                                    distinfo_name + '.dist-info')
+        dist = Distribution(distinfo_dir)
+        resource_path = dist.get_resource_path('babar.png')
+        self.assertEqual(resource_path, 'babar.png')
+        self.assertRaises(KeyError, dist.get_resource_path, 'notexist')
+
+
+class TestEggInfoDistribution(CommonDistributionTests,
+                              support.LoggingCatcher,
+                              unittest.TestCase):
+
+    cls = EggInfoDistribution
+    sample_dist = 'bacon', '0.1', 'bacon-0.1.egg-info'
+
+    def setUp(self):
+        super(TestEggInfoDistribution, self).setUp()
+
+        self.dirs = [os.path.join(self.fake_dists_path, f)
+                     for f in os.listdir(self.fake_dists_path)
+                     if f.endswith('.egg') or f.endswith('.egg-info')]
+
+        self.records = {}
+
+    @unittest.skip('not implemented yet')
+    def test_list_installed_files(self):
+        # EggInfoDistribution defines list_installed_files but there is no
+        # test for it yet; someone with setuptools expertise needs to add a
+        # file with the list of installed files for one of the egg fake dists
+        # and write the support code to populate self.records (and then delete
+        # this method)
+        pass
+
+
+class TestDatabase(support.LoggingCatcher,
+                   unittest.TestCase):
+
+    def setUp(self):
+        super(TestDatabase, self).setUp()
+        disable_cache()
+        # Setup the path environment with our fake distributions
+        current_path = os.path.abspath(os.path.dirname(__file__))
+        self.sys_path = sys.path[:]
+        self.fake_dists_path = os.path.join(current_path, 'fake_dists')
+        sys.path.insert(0, self.fake_dists_path)
+
+    def tearDown(self):
+        sys.path[:] = self.sys_path
+        enable_cache()
+        super(TestDatabase, self).tearDown()
+
+    def test_distinfo_dirname(self):
+        # Given a name and a version, we expect the distinfo_dirname function
+        # to return a standard distribution information directory name.
+
+        items = [
+            # (name, version, standard_dirname)
+            # Test for a very simple single word name and decimal version
+            # number
+            ('docutils', '0.5', 'docutils-0.5.dist-info'),
+            # Test for another except this time with a '-' in the name, which
+            # needs to be transformed during the name lookup
+            ('python-ldap', '2.5', 'python_ldap-2.5.dist-info'),
+            # Test for both '-' in the name and a funky version number
+            ('python-ldap', '2.5 a---5', 'python_ldap-2.5 a---5.dist-info'),
+            ]
+
+        # Loop through the items to validate the results
+        for name, version, standard_dirname in items:
+            dirname = distinfo_dirname(name, version)
+            self.assertEqual(dirname, standard_dirname)
+
+    def test_get_distributions(self):
+        # Lookup all distributions found in the ``sys.path``.
+        # This test could potentially pick up other installed distributions
+        fake_dists = [('grammar', '1.0a4'), ('choxie', '2.0.0.9'),
+                      ('towel-stuff', '0.1'), ('babar', '0.1')]
+        found_dists = []
+
+        # Verify the fake dists have been found.
+        dists = [dist for dist in get_distributions()]
+        for dist in dists:
+            self.assertIsInstance(dist, Distribution)
+            if (dist.name in dict(fake_dists) and
+                dist.path.startswith(self.fake_dists_path)):
+                found_dists.append((dist.name, dist.metadata['version'], ))
+            else:
+                # check that it doesn't find anything more than this
+                self.assertFalse(dist.path.startswith(self.fake_dists_path))
+            # otherwise we don't care what other distributions are found
+
+        # Finally, test that we found all that we were looking for
+        self.assertEqual(sorted(found_dists), sorted(fake_dists))
+
+        # Now, test if the egg-info distributions are found correctly as well
+        fake_dists += [('bacon', '0.1'), ('cheese', '2.0.2'),
+                       ('coconuts-aster', '10.3'),
+                       ('banana', '0.4'), ('strawberry', '0.6'),
+                       ('truffles', '5.0'), ('nut', 'funkyversion')]
+        found_dists = []
+
+        dists = [dist for dist in get_distributions(use_egg_info=True)]
+        for dist in dists:
+            self.assertIsInstance(dist, (Distribution, EggInfoDistribution))
+            if (dist.name in dict(fake_dists) and
+                dist.path.startswith(self.fake_dists_path)):
+                found_dists.append((dist.name, dist.metadata['version']))
+            else:
+                self.assertFalse(dist.path.startswith(self.fake_dists_path))
+
+        self.assertEqual(sorted(fake_dists), sorted(found_dists))
+
+    def test_get_distribution(self):
+        # Test for looking up a distribution by name.
+        # Test the lookup of the towel-stuff distribution
+        name = 'towel-stuff'  # Note: This is different from the directory name
+
+        # Lookup the distribution
+        dist = get_distribution(name)
+        self.assertIsInstance(dist, Distribution)
+        self.assertEqual(dist.name, name)
+
+        # Verify that an unknown distribution returns None
+        self.assertIsNone(get_distribution('bogus'))
+
+        # Verify partial name matching doesn't work
+        self.assertIsNone(get_distribution('towel'))
+
+        # Verify that it does not find egg-info distributions, when not
+        # instructed to
+        self.assertIsNone(get_distribution('bacon'))
+        self.assertIsNone(get_distribution('cheese'))
+        self.assertIsNone(get_distribution('strawberry'))
+        self.assertIsNone(get_distribution('banana'))
+
+        # Now check that it works well in both situations, when egg-info
+        # is a file and directory respectively.
+        dist = get_distribution('cheese', use_egg_info=True)
+        self.assertIsInstance(dist, EggInfoDistribution)
+        self.assertEqual(dist.name, 'cheese')
+
+        dist = get_distribution('bacon', use_egg_info=True)
+        self.assertIsInstance(dist, EggInfoDistribution)
+        self.assertEqual(dist.name, 'bacon')
+
+        dist = get_distribution('banana', use_egg_info=True)
+        self.assertIsInstance(dist, EggInfoDistribution)
+        self.assertEqual(dist.name, 'banana')
+
+        dist = get_distribution('strawberry', use_egg_info=True)
+        self.assertIsInstance(dist, EggInfoDistribution)
+        self.assertEqual(dist.name, 'strawberry')
+
+    def test_get_file_users(self):
+        # Test the iteration of distributions that use a file.
+        name = 'towel_stuff-0.1'
+        path = os.path.join(self.fake_dists_path, name,
+                            'towel_stuff', '__init__.py')
+        for dist in get_file_users(path):
+            self.assertIsInstance(dist, Distribution)
+            self.assertEqual(dist.name, name)
+
+    def test_provides(self):
+        # Test for looking up distributions by what they provide
+        checkLists = lambda x, y: self.assertEqual(sorted(x), sorted(y))
+
+        l = [dist.name for dist in provides_distribution('truffles')]
+        checkLists(l, ['choxie', 'towel-stuff'])
+
+        l = [dist.name for dist in provides_distribution('truffles', '1.0')]
+        checkLists(l, ['choxie'])
+
+        l = [dist.name for dist in provides_distribution('truffles', '1.0',
+                                                         use_egg_info=True)]
+        checkLists(l, ['choxie', 'cheese'])
+
+        l = [dist.name for dist in provides_distribution('truffles', '1.1.2')]
+        checkLists(l, ['towel-stuff'])
+
+        l = [dist.name for dist in provides_distribution('truffles', '1.1')]
+        checkLists(l, ['towel-stuff'])
+
+        l = [dist.name for dist in provides_distribution('truffles',
+                                                         '!=1.1,<=2.0')]
+        checkLists(l, ['choxie'])
+
+        l = [dist.name for dist in provides_distribution('truffles',
+                                                         '!=1.1,<=2.0',
+                                                          use_egg_info=True)]
+        checkLists(l, ['choxie', 'bacon', 'cheese'])
+
+        l = [dist.name for dist in provides_distribution('truffles', '>1.0')]
+        checkLists(l, ['towel-stuff'])
+
+        l = [dist.name for dist in provides_distribution('truffles', '>1.5')]
+        checkLists(l, [])
+
+        l = [dist.name for dist in provides_distribution('truffles', '>1.5',
+                                                         use_egg_info=True)]
+        checkLists(l, ['bacon'])
+
+        l = [dist.name for dist in provides_distribution('truffles', '>=1.0')]
+        checkLists(l, ['choxie', 'towel-stuff'])
+
+        l = [dist.name for dist in provides_distribution('strawberry', '0.6',
+                                                         use_egg_info=True)]
+        checkLists(l, ['coconuts-aster'])
+
+        l = [dist.name for dist in provides_distribution('strawberry', '>=0.5',
+                                                         use_egg_info=True)]
+        checkLists(l, ['coconuts-aster'])
+
+        l = [dist.name for dist in provides_distribution('strawberry', '>0.6',
+                                                         use_egg_info=True)]
+        checkLists(l, [])
+
+        l = [dist.name for dist in provides_distribution('banana', '0.4',
+                                                         use_egg_info=True)]
+        checkLists(l, ['coconuts-aster'])
+
+        l = [dist.name for dist in provides_distribution('banana', '>=0.3',
+                                                         use_egg_info=True)]
+        checkLists(l, ['coconuts-aster'])
+
+        l = [dist.name for dist in provides_distribution('banana', '!=0.4',
+                                                         use_egg_info=True)]
+        checkLists(l, [])
+
+    def test_obsoletes(self):
+        # Test looking for distributions based on what they obsolete
+        checkLists = lambda x, y: self.assertEqual(sorted(x), sorted(y))
+
+        l = [dist.name for dist in obsoletes_distribution('truffles', '1.0')]
+        checkLists(l, [])
+
+        l = [dist.name for dist in obsoletes_distribution('truffles', '1.0',
+                                                          use_egg_info=True)]
+        checkLists(l, ['cheese', 'bacon'])
+
+        l = [dist.name for dist in obsoletes_distribution('truffles', '0.8')]
+        checkLists(l, ['choxie'])
+
+        l = [dist.name for dist in obsoletes_distribution('truffles', '0.8',
+                                                          use_egg_info=True)]
+        checkLists(l, ['choxie', 'cheese'])
+
+        l = [dist.name for dist in obsoletes_distribution('truffles', '0.9.6')]
+        checkLists(l, ['choxie', 'towel-stuff'])
+
+        l = [dist.name for dist in obsoletes_distribution('truffles',
+                                                          '0.5.2.3')]
+        checkLists(l, ['choxie', 'towel-stuff'])
+
+        l = [dist.name for dist in obsoletes_distribution('truffles', '0.2')]
+        checkLists(l, ['towel-stuff'])
+
+    def test_yield_distribution(self):
+        # tests the internal function _yield_distributions
+        checkLists = lambda x, y: self.assertEqual(sorted(x), sorted(y))
+
+        eggs = [('bacon', '0.1'), ('banana', '0.4'), ('strawberry', '0.6'),
+                ('truffles', '5.0'), ('cheese', '2.0.2'),
+                ('coconuts-aster', '10.3'), ('nut', 'funkyversion')]
+        dists = [('choxie', '2.0.0.9'), ('grammar', '1.0a4'),
+                 ('towel-stuff', '0.1'), ('babar', '0.1')]
+
+        checkLists([], _yield_distributions(False, False))
+
+        found = [(dist.name, dist.metadata['Version'])
+                 for dist in _yield_distributions(False, True)
+                 if dist.path.startswith(self.fake_dists_path)]
+        checkLists(eggs, found)
+
+        found = [(dist.name, dist.metadata['Version'])
+                 for dist in _yield_distributions(True, False)
+                 if dist.path.startswith(self.fake_dists_path)]
+        checkLists(dists, found)
+
+        found = [(dist.name, dist.metadata['Version'])
+                 for dist in _yield_distributions(True, True)
+                 if dist.path.startswith(self.fake_dists_path)]
+        checkLists(dists + eggs, found)
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    load = unittest.defaultTestLoader.loadTestsFromTestCase
+    suite.addTest(load(TestDistribution))
+    suite.addTest(load(TestEggInfoDistribution))
+    suite.addTest(load(TestDatabase))
+    return suite
+
+
+if __name__ == "__main__":
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_depgraph.py b/Lib/packaging/tests/test_depgraph.py
new file mode 100644
index 0000000..9271a7b
--- /dev/null
+++ b/Lib/packaging/tests/test_depgraph.py
@@ -0,0 +1,301 @@
+"""Tests for packaging.depgraph """
+import io
+import os
+import re
+import sys
+import packaging.database
+from packaging import depgraph
+
+from packaging.tests import unittest, support
+
+
+class DepGraphTestCase(support.LoggingCatcher,
+                       unittest.TestCase):
+
+    DISTROS_DIST = ('choxie', 'grammar', 'towel-stuff')
+    DISTROS_EGG = ('bacon', 'banana', 'strawberry', 'cheese')
+    BAD_EGGS = ('nut',)
+
+    EDGE = re.compile(
+           r'"(?P<from>.*)" -> "(?P<to>.*)" \[label="(?P<label>.*)"\]')
+
+    def checkLists(self, l1, l2):
+        """ Compare two lists without taking the order into consideration """
+        self.assertListEqual(sorted(l1), sorted(l2))
+
+    def setUp(self):
+        super(DepGraphTestCase, self).setUp()
+        path = os.path.join(os.path.dirname(__file__), 'fake_dists')
+        path = os.path.abspath(path)
+        sys.path.insert(0, path)
+        self.addCleanup(sys.path.remove, path)
+        self.addCleanup(packaging.database.enable_cache)
+        packaging.database.disable_cache()
+
+    def test_generate_graph(self):
+        dists = []
+        for name in self.DISTROS_DIST:
+            dist = packaging.database.get_distribution(name)
+            self.assertNotEqual(dist, None)
+            dists.append(dist)
+
+        choxie, grammar, towel = dists
+
+        graph = depgraph.generate_graph(dists)
+
+        deps = [(x.name, y) for x, y in graph.adjacency_list[choxie]]
+        self.checkLists([('towel-stuff', 'towel-stuff (0.1)')], deps)
+        self.assertIn(choxie, graph.reverse_list[towel])
+        self.checkLists(graph.missing[choxie], ['nut'])
+
+        deps = [(x.name, y) for x, y in graph.adjacency_list[grammar]]
+        self.checkLists([], deps)
+        self.checkLists(graph.missing[grammar], ['truffles (>=1.2)'])
+
+        deps = [(x.name, y) for x, y in graph.adjacency_list[towel]]
+        self.checkLists([], deps)
+        self.checkLists(graph.missing[towel], ['bacon (<=0.2)'])
+
+    def test_generate_graph_egg(self):
+        dists = []
+        for name in self.DISTROS_DIST + self.DISTROS_EGG:
+            dist = packaging.database.get_distribution(name, use_egg_info=True)
+            self.assertNotEqual(dist, None)
+            dists.append(dist)
+
+        choxie, grammar, towel, bacon, banana, strawberry, cheese = dists
+
+        graph = depgraph.generate_graph(dists)
+
+        deps = [(x.name, y) for x, y in graph.adjacency_list[choxie]]
+        self.checkLists([('towel-stuff', 'towel-stuff (0.1)')], deps)
+        self.assertIn(choxie, graph.reverse_list[towel])
+        self.checkLists(graph.missing[choxie], ['nut'])
+
+        deps = [(x.name, y) for x, y in graph.adjacency_list[grammar]]
+        self.checkLists([('bacon', 'truffles (>=1.2)')], deps)
+        self.checkLists(graph.missing[grammar], [])
+        self.assertIn(grammar, graph.reverse_list[bacon])
+
+        deps = [(x.name, y) for x, y in graph.adjacency_list[towel]]
+        self.checkLists([('bacon', 'bacon (<=0.2)')], deps)
+        self.checkLists(graph.missing[towel], [])
+        self.assertIn(towel, graph.reverse_list[bacon])
+
+        deps = [(x.name, y) for x, y in graph.adjacency_list[bacon]]
+        self.checkLists([], deps)
+        self.checkLists(graph.missing[bacon], [])
+
+        deps = [(x.name, y) for x, y in graph.adjacency_list[banana]]
+        self.checkLists([('strawberry', 'strawberry (>=0.5)')], deps)
+        self.checkLists(graph.missing[banana], [])
+        self.assertIn(banana, graph.reverse_list[strawberry])
+
+        deps = [(x.name, y) for x, y in graph.adjacency_list[strawberry]]
+        self.checkLists([], deps)
+        self.checkLists(graph.missing[strawberry], [])
+
+        deps = [(x.name, y) for x, y in graph.adjacency_list[cheese]]
+        self.checkLists([], deps)
+        self.checkLists(graph.missing[cheese], [])
+
+    def test_dependent_dists(self):
+        dists = []
+        for name in self.DISTROS_DIST:
+            dist = packaging.database.get_distribution(name)
+            self.assertNotEqual(dist, None)
+            dists.append(dist)
+
+        choxie, grammar, towel = dists
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, choxie)]
+        self.checkLists([], deps)
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, grammar)]
+        self.checkLists([], deps)
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, towel)]
+        self.checkLists(['choxie'], deps)
+
+    def test_dependent_dists_egg(self):
+        dists = []
+        for name in self.DISTROS_DIST + self.DISTROS_EGG:
+            dist = packaging.database.get_distribution(name, use_egg_info=True)
+            self.assertNotEqual(dist, None)
+            dists.append(dist)
+
+        choxie, grammar, towel, bacon, banana, strawberry, cheese = dists
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, choxie)]
+        self.checkLists([], deps)
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, grammar)]
+        self.checkLists([], deps)
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, towel)]
+        self.checkLists(['choxie'], deps)
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, bacon)]
+        self.checkLists(['choxie', 'towel-stuff', 'grammar'], deps)
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, strawberry)]
+        self.checkLists(['banana'], deps)
+
+        deps = [d.name for d in depgraph.dependent_dists(dists, cheese)]
+        self.checkLists([], deps)
+
+    def test_graph_to_dot(self):
+        expected = (
+            ('towel-stuff', 'bacon', 'bacon (<=0.2)'),
+            ('grammar', 'bacon', 'truffles (>=1.2)'),
+            ('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
+            ('banana', 'strawberry', 'strawberry (>=0.5)'),
+        )
+
+        dists = []
+        for name in self.DISTROS_DIST + self.DISTROS_EGG:
+            dist = packaging.database.get_distribution(name, use_egg_info=True)
+            self.assertNotEqual(dist, None)
+            dists.append(dist)
+
+        graph = depgraph.generate_graph(dists)
+        buf = io.StringIO()
+        depgraph.graph_to_dot(graph, buf)
+        buf.seek(0)
+        matches = []
+        lines = buf.readlines()
+        for line in lines[1:-1]:  # skip the first and the last lines
+            if line[-1] == '\n':
+                line = line[:-1]
+            match = self.EDGE.match(line.strip())
+            self.assertIsNot(match, None)
+            matches.append(match.groups())
+
+        self.checkLists(matches, expected)
+
+    def test_graph_disconnected_to_dot(self):
+        dependencies_expected = (
+            ('towel-stuff', 'bacon', 'bacon (<=0.2)'),
+            ('grammar', 'bacon', 'truffles (>=1.2)'),
+            ('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
+            ('banana', 'strawberry', 'strawberry (>=0.5)'),
+        )
+        disconnected_expected = ('cheese', 'bacon', 'strawberry')
+
+        dists = []
+        for name in self.DISTROS_DIST + self.DISTROS_EGG:
+            dist = packaging.database.get_distribution(name, use_egg_info=True)
+            self.assertNotEqual(dist, None)
+            dists.append(dist)
+
+        graph = depgraph.generate_graph(dists)
+        buf = io.StringIO()
+        depgraph.graph_to_dot(graph, buf, skip_disconnected=False)
+        buf.seek(0)
+        lines = buf.readlines()
+
+        dependencies_lines = []
+        disconnected_lines = []
+
+        # First sort output lines into dependencies and disconnected lines.
+        # We also skip the attribute lines, and don't include the "{" and "}"
+        # lines.
+        disconnected_active = False
+        for line in lines[1:-1]:  # Skip first and last line
+            if line.startswith('subgraph disconnected'):
+                disconnected_active = True
+                continue
+            if line.startswith('}') and disconnected_active:
+                disconnected_active = False
+                continue
+
+            if disconnected_active:
+                # Skip the 'label = "Disconnected"', etc. attribute lines.
+                if ' = ' not in line:
+                    disconnected_lines.append(line)
+            else:
+                dependencies_lines.append(line)
+
+        dependencies_matches = []
+        for line in dependencies_lines:
+            if line[-1] == '\n':
+                line = line[:-1]
+            match = self.EDGE.match(line.strip())
+            self.assertIsNot(match, None)
+            dependencies_matches.append(match.groups())
+
+        disconnected_matches = []
+        for line in disconnected_lines:
+            if line[-1] == '\n':
+                line = line[:-1]
+            line = line.strip('"')
+            disconnected_matches.append(line)
+
+        self.checkLists(dependencies_matches, dependencies_expected)
+        self.checkLists(disconnected_matches, disconnected_expected)
+
+    def test_graph_bad_version_to_dot(self):
+        expected = (
+            ('towel-stuff', 'bacon', 'bacon (<=0.2)'),
+            ('grammar', 'bacon', 'truffles (>=1.2)'),
+            ('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
+            ('banana', 'strawberry', 'strawberry (>=0.5)'),
+        )
+
+        dists = []
+        for name in self.DISTROS_DIST + self.DISTROS_EGG + self.BAD_EGGS:
+            dist = packaging.database.get_distribution(name, use_egg_info=True)
+            self.assertNotEqual(dist, None)
+            dists.append(dist)
+
+        graph = depgraph.generate_graph(dists)
+        buf = io.StringIO()
+        depgraph.graph_to_dot(graph, buf)
+        buf.seek(0)
+        matches = []
+        lines = buf.readlines()
+        for line in lines[1:-1]:  # skip the first and the last lines
+            if line[-1] == '\n':
+                line = line[:-1]
+            match = self.EDGE.match(line.strip())
+            self.assertIsNot(match, None)
+            matches.append(match.groups())
+
+        self.checkLists(matches, expected)
+
+    def test_repr(self):
+        dists = []
+        for name in self.DISTROS_DIST + self.DISTROS_EGG + self.BAD_EGGS:
+            dist = packaging.database.get_distribution(name, use_egg_info=True)
+            self.assertNotEqual(dist, None)
+            dists.append(dist)
+
+        graph = depgraph.generate_graph(dists)
+        self.assertTrue(repr(graph))
+
+    def test_main(self):
+        tempout = io.StringIO()
+        old = sys.stdout
+        sys.stdout = tempout
+        oldargv = sys.argv[:]
+        sys.argv[:] = ['script.py']
+        try:
+            try:
+                depgraph.main()
+            except SystemExit:
+                pass
+        finally:
+            sys.stdout = old
+            sys.argv[:] = oldargv
+
+        # checks what main did XXX could do more here
+        tempout.seek(0)
+        res = tempout.read()
+        self.assertIn('towel', res)
+
+
+def test_suite():
+    return unittest.makeSuite(DepGraphTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_dist.py b/Lib/packaging/tests/test_dist.py
new file mode 100644
index 0000000..77dab71
--- /dev/null
+++ b/Lib/packaging/tests/test_dist.py
@@ -0,0 +1,445 @@
+"""Tests for packaging.dist."""
+import os
+import io
+import sys
+import logging
+import textwrap
+import packaging.dist
+
+from packaging.dist import Distribution
+from packaging.command import set_command
+from packaging.command.cmd import Command
+from packaging.errors import PackagingModuleError, PackagingOptionError
+from packaging.tests import TESTFN, captured_stdout
+from packaging.tests import support, unittest
+from packaging.tests.support import create_distribution
+
+
+class test_dist(Command):
+    """Sample packaging extension command."""
+
+    user_options = [
+        ("sample-option=", "S", "help text"),
+        ]
+
+    def initialize_options(self):
+        self.sample_option = None
+
+    def finalize_options(self):
+        pass
+
+
+class DistributionTestCase(support.TempdirManager,
+                           support.LoggingCatcher,
+                           support.EnvironRestorer,
+                           unittest.TestCase):
+
+    restore_environ = ['HOME']
+
+    def setUp(self):
+        super(DistributionTestCase, self).setUp()
+        self.argv = sys.argv, sys.argv[:]
+        del sys.argv[1:]
+
+    def tearDown(self):
+        sys.argv = self.argv[0]
+        sys.argv[:] = self.argv[1]
+        super(DistributionTestCase, self).tearDown()
+
+    def test_debug_mode(self):
+        self.addCleanup(os.unlink, TESTFN)
+        with open(TESTFN, "w") as f:
+            f.write("[global]\n")
+            f.write("command_packages = foo.bar, splat")
+
+        files = [TESTFN]
+        sys.argv.append("build")
+        __, stdout = captured_stdout(create_distribution, files)
+        self.assertEqual(stdout, '')
+        packaging.dist.DEBUG = True
+        try:
+            __, stdout = captured_stdout(create_distribution, files)
+            self.assertEqual(stdout, '')
+        finally:
+            packaging.dist.DEBUG = False
+
+    def test_write_pkg_file(self):
+        # Check Metadata handling of Unicode fields
+        tmp_dir = self.mkdtemp()
+        my_file = os.path.join(tmp_dir, 'f')
+        cls = Distribution
+
+        dist = cls(attrs={'author': 'Mister Café',
+                          'name': 'my.package',
+                          'maintainer': 'Café Junior',
+                          'summary': 'Café torréfié',
+                          'description': 'Héhéhé'})
+
+        # let's make sure the file can be written
+        # with Unicode fields. they are encoded with
+        # PKG_INFO_ENCODING
+        with open(my_file, 'w') as fp:
+            dist.metadata.write_file(fp)
+
+        # regular ascii is of course always usable
+        dist = cls(attrs={'author': 'Mister Cafe',
+                          'name': 'my.package',
+                          'maintainer': 'Cafe Junior',
+                          'summary': 'Cafe torrefie',
+                          'description': 'Hehehe'})
+
+        with open(my_file, 'w') as fp:
+            dist.metadata.write_file(fp)
+
+    def test_bad_attr(self):
+        Distribution(attrs={'author': 'xxx',
+                            'name': 'xxx',
+                            'version': '1.2',
+                            'url': 'xxxx',
+                            'badoptname': 'xxx'})
+        logs = self.get_logs(logging.WARNING)
+        self.assertEqual(1, len(logs))
+        self.assertIn('unknown argument', logs[0])
+
+    def test_bad_version(self):
+        Distribution(attrs={'author': 'xxx',
+                            'name': 'xxx',
+                            'version': 'xxx',
+                            'url': 'xxxx'})
+        logs = self.get_logs(logging.WARNING)
+        self.assertEqual(1, len(logs))
+        self.assertIn('not a valid version', logs[0])
+
+    def test_empty_options(self):
+        # an empty options dictionary should not stay in the
+        # list of attributes
+        Distribution(attrs={'author': 'xxx',
+                            'name': 'xxx',
+                            'version': '1.2',
+                            'url': 'xxxx',
+                            'options': {}})
+
+        self.assertEqual([], self.get_logs(logging.WARNING))
+
+    def test_non_empty_options(self):
+        # TODO: how to actually use options is not documented except
+        # for a few cryptic comments in dist.py.  If this is to stay
+        # in the public API, it deserves some better documentation.
+
+        # Here is an example of how it's used out there:
+        # http://svn.pythonmac.org/py2app/py2app/trunk/doc/
+        # index.html#specifying-customizations
+        dist = Distribution(attrs={'author': 'xxx',
+                                   'name': 'xxx',
+                                   'version': 'xxx',
+                                   'url': 'xxxx',
+                                   'options': {'sdist': {'owner': 'root'}}})
+
+        self.assertIn('owner', dist.get_option_dict('sdist'))
+
+    def test_finalize_options(self):
+
+        attrs = {'keywords': 'one,two',
+                 'platform': 'one,two'}
+
+        dist = Distribution(attrs=attrs)
+        dist.finalize_options()
+
+        # finalize_option splits platforms and keywords
+        self.assertEqual(dist.metadata['platform'], ['one', 'two'])
+        self.assertEqual(dist.metadata['keywords'], ['one', 'two'])
+
+    def test_find_config_files_disable(self):
+        # Bug #1180: Allow users to disable their own config file.
+        temp_home = self.mkdtemp()
+        if os.name == 'posix':
+            user_filename = os.path.join(temp_home, ".pydistutils.cfg")
+        else:
+            user_filename = os.path.join(temp_home, "pydistutils.cfg")
+
+        with open(user_filename, 'w') as f:
+            f.write('[distutils2]\n')
+
+        def _expander(path):
+            return temp_home
+
+        old_expander = os.path.expanduser
+        os.path.expanduser = _expander
+        try:
+            d = packaging.dist.Distribution()
+            all_files = d.find_config_files()
+
+            d = packaging.dist.Distribution(attrs={'script_args':
+                                                   ['--no-user-cfg']})
+            files = d.find_config_files()
+        finally:
+            os.path.expanduser = old_expander
+
+        # make sure --no-user-cfg disables the user cfg file
+        self.assertEqual((len(all_files) - 1), len(files))
+
+    def test_special_hooks_parsing(self):
+        temp_home = self.mkdtemp()
+        config_files = [os.path.join(temp_home, "config1.cfg"),
+                        os.path.join(temp_home, "config2.cfg")]
+
+        # Store two aliased hooks in config files
+        self.write_file((temp_home, "config1.cfg"),
+                        '[test_dist]\npre-hook.a = type')
+        self.write_file((temp_home, "config2.cfg"),
+                        '[test_dist]\npre-hook.b = type')
+
+        set_command('packaging.tests.test_dist.test_dist')
+        dist = create_distribution(config_files)
+        cmd = dist.get_command_obj("test_dist")
+        self.assertEqual(cmd.pre_hook, {"a": 'type', "b": 'type'})
+
+    def test_hooks_get_run(self):
+        temp_home = self.mkdtemp()
+        config_file = os.path.join(temp_home, "config1.cfg")
+        hooks_module = os.path.join(temp_home, "testhooks.py")
+
+        self.write_file(config_file, textwrap.dedent('''
+            [test_dist]
+            pre-hook.test = testhooks.log_pre_call
+            post-hook.test = testhooks.log_post_call'''))
+
+        self.write_file(hooks_module, textwrap.dedent('''
+        record = []
+
+        def log_pre_call(cmd):
+            record.append('pre-%s' % cmd.get_command_name())
+
+        def log_post_call(cmd):
+            record.append('post-%s' % cmd.get_command_name())
+        '''))
+
+        set_command('packaging.tests.test_dist.test_dist')
+        d = create_distribution([config_file])
+        cmd = d.get_command_obj("test_dist")
+
+        # prepare the call recorders
+        sys.path.append(temp_home)
+        self.addCleanup(sys.path.remove, temp_home)
+        from testhooks import record
+
+        cmd.run = lambda: record.append('run')
+        cmd.finalize_options = lambda: record.append('finalize')
+
+        d.run_command('test_dist')
+
+        self.assertEqual(record, ['finalize',
+                                  'pre-test_dist',
+                                  'run',
+                                  'post-test_dist'])
+
+    def test_hooks_importable(self):
+        temp_home = self.mkdtemp()
+        config_file = os.path.join(temp_home, "config1.cfg")
+
+        self.write_file(config_file, textwrap.dedent('''
+            [test_dist]
+            pre-hook.test = nonexistent.dotted.name'''))
+
+        set_command('packaging.tests.test_dist.test_dist')
+        d = create_distribution([config_file])
+        cmd = d.get_command_obj("test_dist")
+        cmd.ensure_finalized()
+
+        self.assertRaises(PackagingModuleError, d.run_command, 'test_dist')
+
+    def test_hooks_callable(self):
+        temp_home = self.mkdtemp()
+        config_file = os.path.join(temp_home, "config1.cfg")
+
+        self.write_file(config_file, textwrap.dedent('''
+            [test_dist]
+            pre-hook.test = packaging.tests.test_dist.__doc__'''))
+
+        set_command('packaging.tests.test_dist.test_dist')
+        d = create_distribution([config_file])
+        cmd = d.get_command_obj("test_dist")
+        cmd.ensure_finalized()
+
+        self.assertRaises(PackagingOptionError, d.run_command, 'test_dist')
+
+
+class MetadataTestCase(support.TempdirManager,
+                       support.LoggingCatcher,
+                       unittest.TestCase):
+
+    def setUp(self):
+        super(MetadataTestCase, self).setUp()
+        self.argv = sys.argv, sys.argv[:]
+
+    def tearDown(self):
+        sys.argv = self.argv[0]
+        sys.argv[:] = self.argv[1]
+        super(MetadataTestCase, self).tearDown()
+
+    def test_simple_metadata(self):
+        attrs = {"name": "package",
+                 "version": "1.0"}
+        dist = Distribution(attrs)
+        meta = self.format_metadata(dist)
+        self.assertIn("Metadata-Version: 1.0", meta)
+        self.assertNotIn("provides:", meta.lower())
+        self.assertNotIn("requires:", meta.lower())
+        self.assertNotIn("obsoletes:", meta.lower())
+
+    def test_provides_dist(self):
+        attrs = {"name": "package",
+                 "version": "1.0",
+                 "provides_dist": ["package", "package.sub"]}
+        dist = Distribution(attrs)
+        self.assertEqual(dist.metadata['Provides-Dist'],
+                         ["package", "package.sub"])
+        meta = self.format_metadata(dist)
+        self.assertIn("Metadata-Version: 1.2", meta)
+        self.assertNotIn("requires:", meta.lower())
+        self.assertNotIn("obsoletes:", meta.lower())
+
+    def _test_provides_illegal(self):
+        # XXX to do: check the versions
+        self.assertRaises(ValueError, Distribution,
+                          {"name": "package",
+                           "version": "1.0",
+                           "provides_dist": ["my.pkg (splat)"]})
+
+    def test_requires_dist(self):
+        attrs = {"name": "package",
+                 "version": "1.0",
+                 "requires_dist": ["other", "another (==1.0)"]}
+        dist = Distribution(attrs)
+        self.assertEqual(dist.metadata['Requires-Dist'],
+                         ["other", "another (==1.0)"])
+        meta = self.format_metadata(dist)
+        self.assertIn("Metadata-Version: 1.2", meta)
+        self.assertNotIn("provides:", meta.lower())
+        self.assertIn("Requires-Dist: other", meta)
+        self.assertIn("Requires-Dist: another (==1.0)", meta)
+        self.assertNotIn("obsoletes:", meta.lower())
+
+    def _test_requires_illegal(self):
+        # XXX
+        self.assertRaises(ValueError, Distribution,
+                          {"name": "package",
+                           "version": "1.0",
+                           "requires": ["my.pkg (splat)"]})
+
+    def test_obsoletes_dist(self):
+        attrs = {"name": "package",
+                 "version": "1.0",
+                 "obsoletes_dist": ["other", "another (<1.0)"]}
+        dist = Distribution(attrs)
+        self.assertEqual(dist.metadata['Obsoletes-Dist'],
+                         ["other", "another (<1.0)"])
+        meta = self.format_metadata(dist)
+        self.assertIn("Metadata-Version: 1.2", meta)
+        self.assertNotIn("provides:", meta.lower())
+        self.assertNotIn("requires:", meta.lower())
+        self.assertIn("Obsoletes-Dist: other", meta)
+        self.assertIn("Obsoletes-Dist: another (<1.0)", meta)
+
+    def _test_obsoletes_illegal(self):
+        # XXX
+        self.assertRaises(ValueError, Distribution,
+                          {"name": "package",
+                           "version": "1.0",
+                           "obsoletes": ["my.pkg (splat)"]})
+
+    def format_metadata(self, dist):
+        sio = io.StringIO()
+        dist.metadata.write_file(sio)
+        return sio.getvalue()
+
+    def test_custom_pydistutils(self):
+        # fixes #2166
+        # make sure pydistutils.cfg is found
+        if os.name == 'posix':
+            user_filename = ".pydistutils.cfg"
+        else:
+            user_filename = "pydistutils.cfg"
+
+        temp_dir = self.mkdtemp()
+        user_filename = os.path.join(temp_dir, user_filename)
+        with open(user_filename, 'w') as f:
+            f.write('.')
+
+        dist = Distribution()
+
+        # linux-style
+        if sys.platform in ('linux', 'darwin'):
+            os.environ['HOME'] = temp_dir
+            files = dist.find_config_files()
+            self.assertIn(user_filename, files)
+
+        # win32-style
+        if sys.platform == 'win32':
+            # home drive should be found
+            os.environ['HOME'] = temp_dir
+            files = dist.find_config_files()
+            self.assertIn(user_filename, files)
+
+    def test_show_help(self):
+        # smoke test, just makes sure some help is displayed
+        dist = Distribution()
+        sys.argv = []
+        dist.help = True
+        dist.script_name = 'setup.py'
+        __, stdout = captured_stdout(dist.parse_command_line)
+        output = [line for line in stdout.split('\n')
+                  if line.strip() != '']
+        self.assertGreater(len(output), 0)
+
+    def test_description(self):
+        desc = textwrap.dedent("""\
+        example::
+              We start here
+            and continue here
+          and end here.""")
+        attrs = {"name": "package",
+                 "version": "1.0",
+                 "description": desc}
+
+        dist = packaging.dist.Distribution(attrs)
+        meta = self.format_metadata(dist)
+        meta = meta.replace('\n' + 7 * ' ' + '|', '\n')
+        self.assertIn(desc, meta)
+
+    def test_read_metadata(self):
+        attrs = {"name": "package",
+                 "version": "1.0",
+                 "description": "desc",
+                 "summary": "xxx",
+                 "download_url": "http://example.com",
+                 "keywords": ['one', 'two'],
+                 "requires_dist": ['foo']}
+
+        dist = Distribution(attrs)
+        metadata = dist.metadata
+
+        # write it then reloads it
+        PKG_INFO = io.StringIO()
+        metadata.write_file(PKG_INFO)
+        PKG_INFO.seek(0)
+
+        metadata.read_file(PKG_INFO)
+        self.assertEqual(metadata['name'], "package")
+        self.assertEqual(metadata['version'], "1.0")
+        self.assertEqual(metadata['summary'], "xxx")
+        self.assertEqual(metadata['download_url'], 'http://example.com')
+        self.assertEqual(metadata['keywords'], ['one', 'two'])
+        self.assertEqual(metadata['platform'], [])
+        self.assertEqual(metadata['obsoletes'], [])
+        self.assertEqual(metadata['requires-dist'], ['foo'])
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(DistributionTestCase))
+    suite.addTest(unittest.makeSuite(MetadataTestCase))
+    return suite
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_extension.py b/Lib/packaging/tests/test_extension.py
new file mode 100644
index 0000000..41182e5
--- /dev/null
+++ b/Lib/packaging/tests/test_extension.py
@@ -0,0 +1,15 @@
+"""Tests for packaging.extension."""
+import os
+
+from packaging.compiler.extension import Extension
+from packaging.tests import unittest
+
+class ExtensionTestCase(unittest.TestCase):
+
+    pass
+
+def test_suite():
+    return unittest.makeSuite(ExtensionTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_install.py b/Lib/packaging/tests/test_install.py
new file mode 100644
index 0000000..2c51d19
--- /dev/null
+++ b/Lib/packaging/tests/test_install.py
@@ -0,0 +1,353 @@
+"""Tests for the packaging.install module."""
+
+import os
+from tempfile import mkstemp
+from packaging import install
+from packaging.pypi.xmlrpc import Client
+from packaging.metadata import Metadata
+
+from packaging.tests.support import LoggingCatcher, TempdirManager, unittest
+from packaging.tests.pypi_server import use_xmlrpc_server
+
+
+class InstalledDist:
+    """Distribution object, represent distributions currently installed on the
+    system"""
+    def __init__(self, name, version, deps):
+        self.metadata = Metadata()
+        self.name = name
+        self.version = version
+        self.metadata['Name'] = name
+        self.metadata['Version'] = version
+        self.metadata['Requires-Dist'] = deps
+
+    def __repr__(self):
+        return '<InstalledDist %s>' % self.metadata['Name']
+
+
+class ToInstallDist:
+    """Distribution that will be installed"""
+
+    def __init__(self, files=False):
+        self._files = files
+        self.install_called = False
+        self.install_called_with = {}
+        self.uninstall_called = False
+        self._real_files = []
+        self.name = "fake"
+        self.version = "fake"
+        if files:
+            for f in range(0, 3):
+                self._real_files.append(mkstemp())
+
+    def _unlink_installed_files(self):
+        if self._files:
+            for f in self._real_files:
+                os.unlink(f[1])
+
+    def list_installed_files(self, **args):
+        if self._files:
+            return [f[1] for f in self._real_files]
+
+    def get_install(self, **args):
+        return self.list_installed_files()
+
+
+class MagicMock:
+    def __init__(self, return_value=None, raise_exception=False):
+        self.called = False
+        self._times_called = 0
+        self._called_with = []
+        self._return_value = return_value
+        self._raise = raise_exception
+
+    def __call__(self, *args, **kwargs):
+        self.called = True
+        self._times_called = self._times_called + 1
+        self._called_with.append((args, kwargs))
+        iterable = hasattr(self._raise, '__iter__')
+        if self._raise:
+            if ((not iterable and self._raise)
+                    or self._raise[self._times_called - 1]):
+                raise Exception
+        return self._return_value
+
+    def called_with(self, *args, **kwargs):
+        return (args, kwargs) in self._called_with
+
+
+def get_installed_dists(dists):
+    """Return a list of fake installed dists.
+    The list is name, version, deps"""
+    objects = []
+    for name, version, deps in dists:
+        objects.append(InstalledDist(name, version, deps))
+    return objects
+
+
+class TestInstall(LoggingCatcher, TempdirManager, unittest.TestCase):
+    def _get_client(self, server, *args, **kwargs):
+        return Client(server.full_address, *args, **kwargs)
+
+    def _get_results(self, output):
+        """return a list of results"""
+        installed = [(o.name, str(o.version)) for o in output['install']]
+        remove = [(o.name, str(o.version)) for o in output['remove']]
+        conflict = [(o.name, str(o.version)) for o in output['conflict']]
+        return installed, remove, conflict
+
+    @use_xmlrpc_server()
+    def test_existing_deps(self, server):
+        # Test that the installer get the dependencies from the metadatas
+        # and ask the index for this dependencies.
+        # In this test case, we have choxie that is dependent from towel-stuff
+        # 0.1, which is in-turn dependent on bacon <= 0.2:
+        # choxie -> towel-stuff -> bacon.
+        # Each release metadata is not provided in metadata 1.2.
+        client = self._get_client(server)
+        archive_path = '%s/distribution.tar.gz' % server.full_address
+        server.xmlrpc.set_distributions([
+            {'name': 'choxie',
+             'version': '2.0.0.9',
+             'requires_dist': ['towel-stuff (0.1)'],
+             'url': archive_path},
+            {'name': 'towel-stuff',
+             'version': '0.1',
+             'requires_dist': ['bacon (<= 0.2)'],
+             'url': archive_path},
+            {'name': 'bacon',
+             'version': '0.1',
+             'requires_dist': [],
+             'url': archive_path},
+            ])
+        installed = get_installed_dists([('bacon', '0.1', [])])
+        output = install.get_infos("choxie", index=client,
+                                   installed=installed)
+
+        # we don't have installed bacon as it's already installed system-wide
+        self.assertEqual(0, len(output['remove']))
+        self.assertEqual(2, len(output['install']))
+        readable_output = [(o.name, str(o.version))
+                           for o in output['install']]
+        self.assertIn(('towel-stuff', '0.1'), readable_output)
+        self.assertIn(('choxie', '2.0.0.9'), readable_output)
+
+    @use_xmlrpc_server()
+    def test_upgrade_existing_deps(self, server):
+        client = self._get_client(server)
+        archive_path = '%s/distribution.tar.gz' % server.full_address
+        server.xmlrpc.set_distributions([
+            {'name': 'choxie',
+             'version': '2.0.0.9',
+             'requires_dist': ['towel-stuff (0.1)'],
+             'url': archive_path},
+            {'name': 'towel-stuff',
+             'version': '0.1',
+             'requires_dist': ['bacon (>= 0.2)'],
+             'url': archive_path},
+            {'name': 'bacon',
+             'version': '0.2',
+             'requires_dist': [],
+             'url': archive_path},
+            ])
+
+        output = install.get_infos("choxie", index=client,
+                     installed=get_installed_dists([('bacon', '0.1', [])]))
+        installed = [(o.name, str(o.version)) for o in output['install']]
+
+        # we need bacon 0.2, but 0.1 is installed.
+        # So we expect to remove 0.1 and to install 0.2 instead.
+        remove = [(o.name, str(o.version)) for o in output['remove']]
+        self.assertIn(('choxie', '2.0.0.9'), installed)
+        self.assertIn(('towel-stuff', '0.1'), installed)
+        self.assertIn(('bacon', '0.2'), installed)
+        self.assertIn(('bacon', '0.1'), remove)
+        self.assertEqual(0, len(output['conflict']))
+
+    @use_xmlrpc_server()
+    def test_conflicts(self, server):
+        # Tests that conflicts are detected
+        client = self._get_client(server)
+        archive_path = '%s/distribution.tar.gz' % server.full_address
+
+        # choxie depends on towel-stuff, which depends on bacon.
+        server.xmlrpc.set_distributions([
+            {'name': 'choxie',
+             'version': '2.0.0.9',
+             'requires_dist': ['towel-stuff (0.1)'],
+             'url': archive_path},
+            {'name': 'towel-stuff',
+             'version': '0.1',
+             'requires_dist': ['bacon (>= 0.2)'],
+             'url': archive_path},
+            {'name': 'bacon',
+             'version': '0.2',
+             'requires_dist': [],
+             'url': archive_path},
+            ])
+
+        # name, version, deps.
+        already_installed = [('bacon', '0.1', []),
+                             ('chicken', '1.1', ['bacon (0.1)'])]
+        output = install.get_infos(
+            'choxie', index=client,
+            installed=get_installed_dists(already_installed))
+
+        # we need bacon 0.2, but 0.1 is installed.
+        # So we expect to remove 0.1 and to install 0.2 instead.
+        installed, remove, conflict = self._get_results(output)
+        self.assertIn(('choxie', '2.0.0.9'), installed)
+        self.assertIn(('towel-stuff', '0.1'), installed)
+        self.assertIn(('bacon', '0.2'), installed)
+        self.assertIn(('bacon', '0.1'), remove)
+        self.assertIn(('chicken', '1.1'), conflict)
+
+    @use_xmlrpc_server()
+    def test_installation_unexisting_project(self, server):
+        # Test that the isntalled raises an exception if the project does not
+        # exists.
+        client = self._get_client(server)
+        self.assertRaises(install.InstallationException,
+                          install.get_infos,
+                          'unexisting project', index=client)
+
+    def test_move_files(self):
+        # test that the files are really moved, and that the new path is
+        # returned.
+        path = self.mkdtemp()
+        newpath = self.mkdtemp()
+        files = [os.path.join(path, str(x)) for x in range(1, 20)]
+        for f in files:
+            open(f, 'a+').close()
+        output = [o for o in install._move_files(files, newpath)]
+
+        # check that output return the list of old/new places
+        for f in files:
+            self.assertIn((f, '%s%s' % (newpath, f)), output)
+
+        # remove the files
+        for f in [o[1] for o in output]:  # o[1] is the new place
+            os.remove(f)
+
+    def test_update_infos(self):
+        tests = [[
+            {'foo': ['foobar', 'foo', 'baz'], 'baz': ['foo', 'foo']},
+            {'foo': ['additional_content', 'yeah'], 'baz': ['test', 'foo']},
+            {'foo': ['foobar', 'foo', 'baz', 'additional_content', 'yeah'],
+             'baz': ['foo', 'foo', 'test', 'foo']},
+        ]]
+
+        for dict1, dict2, expect in tests:
+            install._update_infos(dict1, dict2)
+            for key in expect:
+                self.assertEqual(expect[key], dict1[key])
+
+    def test_install_dists_rollback(self):
+        # if one of the distribution installation fails, call uninstall on all
+        # installed distributions.
+
+        old_install_dist = install._install_dist
+        old_uninstall = getattr(install, 'uninstall', None)
+
+        install._install_dist = MagicMock(return_value=[],
+                                          raise_exception=(False, True))
+        install.remove = MagicMock()
+        try:
+            d1 = ToInstallDist()
+            d2 = ToInstallDist()
+            path = self.mkdtemp()
+            self.assertRaises(Exception, install.install_dists, [d1, d2], path)
+            self.assertTrue(install._install_dist.called_with(d1, path))
+            self.assertTrue(install.remove.called)
+        finally:
+            install._install_dist = old_install_dist
+            install.remove = old_uninstall
+
+    def test_install_dists_success(self):
+        old_install_dist = install._install_dist
+        install._install_dist = MagicMock(return_value=[])
+        try:
+            # test that the install method is called on each distributions
+            d1 = ToInstallDist()
+            d2 = ToInstallDist()
+
+            # should call install
+            path = self.mkdtemp()
+            install.install_dists([d1, d2], path)
+            for dist in (d1, d2):
+                self.assertTrue(install._install_dist.called_with(dist, path))
+        finally:
+            install._install_dist = old_install_dist
+
+    def test_install_from_infos_conflict(self):
+        # assert conflicts raise an exception
+        self.assertRaises(install.InstallationConflict,
+            install.install_from_infos,
+            conflicts=[ToInstallDist()])
+
+    def test_install_from_infos_remove_success(self):
+        old_install_dists = install.install_dists
+        install.install_dists = lambda x, y=None: None
+        try:
+            dists = []
+            for i in range(2):
+                dists.append(ToInstallDist(files=True))
+            install.install_from_infos(remove=dists)
+
+            # assert that the files have been removed
+            for dist in dists:
+                for f in dist.list_installed_files():
+                    self.assertFalse(os.path.exists(f))
+        finally:
+            install.install_dists = old_install_dists
+
+    def test_install_from_infos_remove_rollback(self):
+        old_install_dist = install._install_dist
+        old_uninstall = getattr(install, 'uninstall', None)
+
+        install._install_dist = MagicMock(return_value=[],
+                raise_exception=(False, True))
+        install.uninstall = MagicMock()
+        try:
+            # assert that if an error occurs, the removed files are restored.
+            remove = []
+            for i in range(2):
+                remove.append(ToInstallDist(files=True))
+            to_install = [ToInstallDist(), ToInstallDist()]
+            temp_dir = self.mkdtemp()
+
+            self.assertRaises(Exception, install.install_from_infos,
+                              install_path=temp_dir, install=to_install,
+                              remove=remove)
+            # assert that the files are in the same place
+            # assert that the files have been removed
+            for dist in remove:
+                for f in dist.list_installed_files():
+                    self.assertTrue(os.path.exists(f))
+                dist._unlink_installed_files()
+        finally:
+            install.install_dist = old_install_dist
+            install.uninstall = old_uninstall
+
+    def test_install_from_infos_install_succes(self):
+        old_install_dist = install._install_dist
+        install._install_dist = MagicMock([])
+        try:
+            # assert that the distribution can be installed
+            install_path = "my_install_path"
+            to_install = [ToInstallDist(), ToInstallDist()]
+
+            install.install_from_infos(install_path, install=to_install)
+            for dist in to_install:
+                install._install_dist.called_with(install_path)
+        finally:
+            install._install_dist = old_install_dist
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestInstall))
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_manifest.py b/Lib/packaging/tests/test_manifest.py
new file mode 100644
index 0000000..21a42c3
--- /dev/null
+++ b/Lib/packaging/tests/test_manifest.py
@@ -0,0 +1,72 @@
+"""Tests for packaging.manifest."""
+import os
+import logging
+from io import StringIO
+from packaging.manifest import Manifest
+
+from packaging.tests import unittest, support
+
+_MANIFEST = """\
+recursive-include foo *.py   # ok
+# nothing here
+
+#
+
+recursive-include bar \\
+  *.dat   *.txt
+"""
+
+_MANIFEST2 = """\
+README
+file1
+"""
+
+
+class ManifestTestCase(support.TempdirManager,
+                       support.LoggingCatcher,
+                       unittest.TestCase):
+
+    def test_manifest_reader(self):
+        tmpdir = self.mkdtemp()
+        MANIFEST = os.path.join(tmpdir, 'MANIFEST.in')
+        with open(MANIFEST, 'w') as f:
+            f.write(_MANIFEST)
+
+        manifest = Manifest()
+        manifest.read_template(MANIFEST)
+
+        warnings = self.get_logs(logging.WARNING)
+        # the manifest should have been read and 3 warnings issued
+        # (we didn't provide the files)
+        self.assertEqual(3, len(warnings))
+        for warning in warnings:
+            self.assertIn('no files found matching', warning)
+
+        # reset logs for the next assert
+        self.loghandler.flush()
+
+        # manifest also accepts file-like objects
+        with open(MANIFEST) as f:
+            manifest.read_template(f)
+
+        # the manifest should have been read and 3 warnings issued
+        # (we didn't provide the files)
+        self.assertEqual(3, len(warnings))
+
+    def test_default_actions(self):
+        tmpdir = self.mkdtemp()
+        self.addCleanup(os.chdir, os.getcwd())
+        os.chdir(tmpdir)
+        self.write_file('README', 'xxx')
+        self.write_file('file1', 'xxx')
+        content = StringIO(_MANIFEST2)
+        manifest = Manifest()
+        manifest.read_template(content)
+        self.assertEqual(['README', 'file1'], manifest.files)
+
+
+def test_suite():
+    return unittest.makeSuite(ManifestTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_markers.py b/Lib/packaging/tests/test_markers.py
new file mode 100644
index 0000000..dec0429
--- /dev/null
+++ b/Lib/packaging/tests/test_markers.py
@@ -0,0 +1,71 @@
+"""Tests for packaging.markers."""
+import os
+import sys
+import platform
+from packaging.markers import interpret
+
+from packaging.tests import unittest
+from packaging.tests.support import LoggingCatcher
+
+
+class MarkersTestCase(LoggingCatcher,
+                      unittest.TestCase):
+
+    def test_interpret(self):
+        sys_platform = sys.platform
+        version = sys.version.split()[0]
+        os_name = os.name
+        platform_version = platform.version()
+        platform_machine = platform.machine()
+        platform_python_implementation = platform.python_implementation()
+
+        self.assertTrue(interpret("sys.platform == '%s'" % sys_platform))
+        self.assertTrue(interpret(
+            "sys.platform == '%s' or python_version == '2.4'" % sys_platform))
+        self.assertTrue(interpret(
+            "sys.platform == '%s' and python_full_version == '%s'" %
+            (sys_platform, version)))
+        self.assertTrue(interpret("'%s' == sys.platform" % sys_platform))
+        self.assertTrue(interpret('os.name == "%s"' % os_name))
+        self.assertTrue(interpret(
+            'platform.version == "%s" and platform.machine == "%s"' %
+            (platform_version, platform_machine)))
+        self.assertTrue(interpret('platform.python_implementation == "%s"' %
+            platform_python_implementation))
+
+        # stuff that need to raise a syntax error
+        ops = ('os.name == os.name', 'os.name == 2', "'2' == '2'",
+               'okpjonon', '', 'os.name ==', 'python_version == 2.4')
+        for op in ops:
+            self.assertRaises(SyntaxError, interpret, op)
+
+        # combined operations
+        OP = 'os.name == "%s"' % os_name
+        AND = ' and '
+        OR = ' or '
+        self.assertTrue(interpret(OP + AND + OP))
+        self.assertTrue(interpret(OP + AND + OP + AND + OP))
+        self.assertTrue(interpret(OP + OR + OP))
+        self.assertTrue(interpret(OP + OR + OP + OR + OP))
+
+        # other operators
+        self.assertTrue(interpret("os.name != 'buuuu'"))
+        self.assertTrue(interpret("python_version > '1.0'"))
+        self.assertTrue(interpret("python_version < '5.0'"))
+        self.assertTrue(interpret("python_version <= '5.0'"))
+        self.assertTrue(interpret("python_version >= '1.0'"))
+        self.assertTrue(interpret("'%s' in os.name" % os_name))
+        self.assertTrue(interpret("'buuuu' not in os.name"))
+        self.assertTrue(interpret(
+            "'buuuu' not in os.name and '%s' in os.name" % os_name))
+
+        # execution context
+        self.assertTrue(interpret('python_version == "0.1"',
+                                  {'python_version': '0.1'}))
+
+
+def test_suite():
+    return unittest.makeSuite(MarkersTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_metadata.py b/Lib/packaging/tests/test_metadata.py
new file mode 100644
index 0000000..b8dc5d8
--- /dev/null
+++ b/Lib/packaging/tests/test_metadata.py
@@ -0,0 +1,279 @@
+"""Tests for packaging.metadata."""
+import os
+import sys
+import logging
+from io import StringIO
+
+from packaging.errors import (MetadataConflictError, MetadataMissingError,
+                              MetadataUnrecognizedVersionError)
+from packaging.metadata import Metadata, PKG_INFO_PREFERRED_VERSION
+
+from packaging.tests import unittest
+from packaging.tests.support import LoggingCatcher
+
+
+class MetadataTestCase(LoggingCatcher,
+                       unittest.TestCase):
+
+    def test_instantiation(self):
+        PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
+        with open(PKG_INFO, 'r') as f:
+            contents = f.read()
+        fp = StringIO(contents)
+
+        m = Metadata()
+        self.assertRaises(MetadataUnrecognizedVersionError, m.items)
+
+        m = Metadata(PKG_INFO)
+        self.assertEqual(len(m.items()), 22)
+
+        m = Metadata(fileobj=fp)
+        self.assertEqual(len(m.items()), 22)
+
+        m = Metadata(mapping=dict(name='Test', version='1.0'))
+        self.assertEqual(len(m.items()), 11)
+
+        d = dict(m.items())
+        self.assertRaises(TypeError, Metadata,
+                          PKG_INFO, fileobj=fp)
+        self.assertRaises(TypeError, Metadata,
+                          PKG_INFO, mapping=d)
+        self.assertRaises(TypeError, Metadata,
+                          fileobj=fp, mapping=d)
+        self.assertRaises(TypeError, Metadata,
+                          PKG_INFO, mapping=m, fileobj=fp)
+
+    def test_metadata_read_write(self):
+        PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
+        metadata = Metadata(PKG_INFO)
+        out = StringIO()
+        metadata.write_file(out)
+        out.seek(0)
+        res = Metadata()
+        res.read_file(out)
+        for k in metadata:
+            self.assertEqual(metadata[k], res[k])
+
+    def test_metadata_markers(self):
+        # see if we can be platform-aware
+        PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
+        with open(PKG_INFO, 'r') as f:
+            content = f.read() % sys.platform
+        metadata = Metadata(platform_dependent=True)
+
+        metadata.read_file(StringIO(content))
+        self.assertEqual(metadata['Requires-Dist'], ['bar'])
+        metadata['Name'] = "baz; sys.platform == 'blah'"
+        # FIXME is None or 'UNKNOWN' correct here?
+        # where is that documented?
+        self.assertEqual(metadata['Name'], None)
+
+        # test with context
+        context = {'sys.platform': 'okook'}
+        metadata = Metadata(platform_dependent=True,
+                                        execution_context=context)
+        metadata.read_file(StringIO(content))
+        self.assertEqual(metadata['Requires-Dist'], ['foo'])
+
+    def test_description(self):
+        PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
+        with open(PKG_INFO, 'r') as f:
+            content = f.read() % sys.platform
+        metadata = Metadata()
+        metadata.read_file(StringIO(content))
+
+        # see if we can read the description now
+        DESC = os.path.join(os.path.dirname(__file__), 'LONG_DESC.txt')
+        with open(DESC) as f:
+            wanted = f.read()
+        self.assertEqual(wanted, metadata['Description'])
+
+        # save the file somewhere and make sure we can read it back
+        out = StringIO()
+        metadata.write_file(out)
+        out.seek(0)
+        metadata.read_file(out)
+        self.assertEqual(wanted, metadata['Description'])
+
+    def test_mapping_api(self):
+        PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
+        with open(PKG_INFO, 'r') as f:
+            content = f.read() % sys.platform
+        metadata = Metadata(fileobj=StringIO(content))
+        self.assertIn('Version', metadata.keys())
+        self.assertIn('0.5', metadata.values())
+        self.assertIn(('Version', '0.5'), metadata.items())
+
+        metadata.update({'version': '0.6'})
+        self.assertEqual(metadata['Version'], '0.6')
+        metadata.update([('version', '0.7')])
+        self.assertEqual(metadata['Version'], '0.7')
+
+        self.assertEqual(list(metadata), list(metadata.keys()))
+
+    def test_versions(self):
+        metadata = Metadata()
+        metadata['Obsoletes'] = 'ok'
+        self.assertEqual(metadata['Metadata-Version'], '1.1')
+
+        del metadata['Obsoletes']
+        metadata['Obsoletes-Dist'] = 'ok'
+        self.assertEqual(metadata['Metadata-Version'], '1.2')
+
+        self.assertRaises(MetadataConflictError, metadata.set,
+                          'Obsoletes', 'ok')
+
+        del metadata['Obsoletes']
+        del metadata['Obsoletes-Dist']
+        metadata['Version'] = '1'
+        self.assertEqual(metadata['Metadata-Version'], '1.0')
+
+        PKG_INFO = os.path.join(os.path.dirname(__file__),
+                                'SETUPTOOLS-PKG-INFO')
+        with open(PKG_INFO, 'r') as f:
+            content = f.read()
+        metadata.read_file(StringIO(content))
+        self.assertEqual(metadata['Metadata-Version'], '1.0')
+
+        PKG_INFO = os.path.join(os.path.dirname(__file__),
+                                'SETUPTOOLS-PKG-INFO2')
+        with open(PKG_INFO, 'r') as f:
+            content = f.read()
+        metadata.read_file(StringIO(content))
+        self.assertEqual(metadata['Metadata-Version'], '1.1')
+
+        # Update the _fields dict directly to prevent 'Metadata-Version'
+        # from being updated by the _set_best_version() method.
+        metadata._fields['Metadata-Version'] = '1.618'
+        self.assertRaises(MetadataUnrecognizedVersionError, metadata.keys)
+
+    def test_warnings(self):
+        metadata = Metadata()
+
+        # these should raise a warning
+        values = (('Requires-Dist', 'Funky (Groovie)'),
+                  ('Requires-Python', '1-4'))
+
+        for name, value in values:
+            metadata.set(name, value)
+
+        # we should have a certain amount of warnings
+        self.assertEqual(len(self.get_logs()), 2)
+
+    def test_multiple_predicates(self):
+        metadata = Metadata()
+
+        # see for "3" instead of "3.0"  ???
+        # its seems like the MINOR VERSION can be omitted
+        metadata['Requires-Python'] = '>=2.6, <3.0'
+        metadata['Requires-Dist'] = ['Foo (>=2.6, <3.0)']
+
+        self.assertEqual([], self.get_logs(logging.WARNING))
+
+    def test_project_url(self):
+        metadata = Metadata()
+        metadata['Project-URL'] = [('one', 'http://ok')]
+        self.assertEqual(metadata['Project-URL'],
+                          [('one', 'http://ok')])
+        self.assertEqual(metadata['Metadata-Version'], '1.2')
+
+    def test_check_version(self):
+        metadata = Metadata()
+        metadata['Name'] = 'vimpdb'
+        metadata['Home-page'] = 'http://pypi.python.org'
+        metadata['Author'] = 'Monty Python'
+        metadata.docutils_support = False
+        missing, warnings = metadata.check()
+        self.assertEqual(missing, ['Version'])
+
+    def test_check_version_strict(self):
+        metadata = Metadata()
+        metadata['Name'] = 'vimpdb'
+        metadata['Home-page'] = 'http://pypi.python.org'
+        metadata['Author'] = 'Monty Python'
+        metadata.docutils_support = False
+        self.assertRaises(MetadataMissingError, metadata.check, strict=True)
+
+    def test_check_name(self):
+        metadata = Metadata()
+        metadata['Version'] = '1.0'
+        metadata['Home-page'] = 'http://pypi.python.org'
+        metadata['Author'] = 'Monty Python'
+        metadata.docutils_support = False
+        missing, warnings = metadata.check()
+        self.assertEqual(missing, ['Name'])
+
+    def test_check_name_strict(self):
+        metadata = Metadata()
+        metadata['Version'] = '1.0'
+        metadata['Home-page'] = 'http://pypi.python.org'
+        metadata['Author'] = 'Monty Python'
+        metadata.docutils_support = False
+        self.assertRaises(MetadataMissingError, metadata.check, strict=True)
+
+    def test_check_author(self):
+        metadata = Metadata()
+        metadata['Version'] = '1.0'
+        metadata['Name'] = 'vimpdb'
+        metadata['Home-page'] = 'http://pypi.python.org'
+        metadata.docutils_support = False
+        missing, warnings = metadata.check()
+        self.assertEqual(missing, ['Author'])
+
+    def test_check_homepage(self):
+        metadata = Metadata()
+        metadata['Version'] = '1.0'
+        metadata['Name'] = 'vimpdb'
+        metadata['Author'] = 'Monty Python'
+        metadata.docutils_support = False
+        missing, warnings = metadata.check()
+        self.assertEqual(missing, ['Home-page'])
+
+    def test_check_predicates(self):
+        metadata = Metadata()
+        metadata['Version'] = 'rr'
+        metadata['Name'] = 'vimpdb'
+        metadata['Home-page'] = 'http://pypi.python.org'
+        metadata['Author'] = 'Monty Python'
+        metadata['Requires-dist'] = ['Foo (a)']
+        metadata['Obsoletes-dist'] = ['Foo (a)']
+        metadata['Provides-dist'] = ['Foo (a)']
+        if metadata.docutils_support:
+            missing, warnings = metadata.check()
+            self.assertEqual(len(warnings), 4)
+            metadata.docutils_support = False
+        missing, warnings = metadata.check()
+        self.assertEqual(len(warnings), 4)
+
+    def test_best_choice(self):
+        metadata = Metadata()
+        metadata['Version'] = '1.0'
+        self.assertEqual(metadata['Metadata-Version'],
+                         PKG_INFO_PREFERRED_VERSION)
+        metadata['Classifier'] = ['ok']
+        self.assertEqual(metadata['Metadata-Version'], '1.2')
+
+    def test_project_urls(self):
+        # project-url is a bit specific, make sure we write it
+        # properly in PKG-INFO
+        metadata = Metadata()
+        metadata['Version'] = '1.0'
+        metadata['Project-Url'] = [('one', 'http://ok')]
+        self.assertEqual(metadata['Project-Url'], [('one', 'http://ok')])
+        file_ = StringIO()
+        metadata.write_file(file_)
+        file_.seek(0)
+        res = file_.read().split('\n')
+        self.assertIn('Project-URL: one,http://ok', res)
+
+        file_.seek(0)
+        metadata = Metadata()
+        metadata.read_file(file_)
+        self.assertEqual(metadata['Project-Url'], [('one', 'http://ok')])
+
+
+def test_suite():
+    return unittest.makeSuite(MetadataTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_mixin2to3.py b/Lib/packaging/tests/test_mixin2to3.py
new file mode 100644
index 0000000..d7c83c2
--- /dev/null
+++ b/Lib/packaging/tests/test_mixin2to3.py
@@ -0,0 +1,75 @@
+"""Tests for packaging.command.build_py."""
+import sys
+
+from packaging.tests import unittest, support
+from packaging.compat import Mixin2to3
+
+
+class Mixin2to3TestCase(support.TempdirManager,
+                        support.LoggingCatcher,
+                        unittest.TestCase):
+
+    @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+    def test_convert_code_only(self):
+        # used to check if code gets converted properly.
+        code_content = "print 'test'\n"
+        code_handle = self.mktempfile()
+        code_name = code_handle.name
+
+        code_handle.write(code_content)
+        code_handle.flush()
+
+        mixin2to3 = Mixin2to3()
+        mixin2to3._run_2to3([code_name])
+        converted_code_content = "print('test')\n"
+        with open(code_name) as fp:
+            new_code_content = "".join(fp.readlines())
+
+        self.assertEqual(new_code_content, converted_code_content)
+
+    @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+    def test_doctests_only(self):
+        # used to check if doctests gets converted properly.
+        doctest_content = '"""\n>>> print test\ntest\n"""\nprint test\n\n'
+        doctest_handle = self.mktempfile()
+        doctest_name = doctest_handle.name
+
+        doctest_handle.write(doctest_content)
+        doctest_handle.flush()
+
+        mixin2to3 = Mixin2to3()
+        mixin2to3._run_2to3([doctest_name])
+
+        converted_doctest_content = ['"""', '>>> print(test)', 'test', '"""',
+                                     'print(test)', '', '', '']
+        converted_doctest_content = '\n'.join(converted_doctest_content)
+        with open(doctest_name) as fp:
+            new_doctest_content = "".join(fp.readlines())
+
+        self.assertEqual(new_doctest_content, converted_doctest_content)
+
+    @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+    def test_additional_fixers(self):
+        # used to check if use_2to3_fixers works
+        code_content = "type(x) is T"
+        code_handle = self.mktempfile()
+        code_name = code_handle.name
+
+        code_handle.write(code_content)
+        code_handle.flush()
+
+        mixin2to3 = Mixin2to3()
+
+        mixin2to3._run_2to3(files=[code_name], doctests=[code_name],
+                            fixers=['packaging.tests.fixer'])
+        converted_code_content = "isinstance(x, T)"
+        with open(code_name) as fp:
+            new_code_content = "".join(fp.readlines())
+        self.assertEqual(new_code_content, converted_code_content)
+
+
+def test_suite():
+    return unittest.makeSuite(Mixin2to3TestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_msvc9compiler.py b/Lib/packaging/tests/test_msvc9compiler.py
new file mode 100644
index 0000000..dc3ae65
--- /dev/null
+++ b/Lib/packaging/tests/test_msvc9compiler.py
@@ -0,0 +1,140 @@
+"""Tests for packaging.compiler.msvc9compiler."""
+import os
+import sys
+
+from packaging.errors import PackagingPlatformError
+
+from packaging.tests import unittest, support
+
+_MANIFEST = """\
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
+          manifestVersion="1.0">
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+    <security>
+      <requestedPrivileges>
+        <requestedExecutionLevel level="asInvoker" uiAccess="false">
+        </requestedExecutionLevel>
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity type="win32" name="Microsoft.VC90.CRT"
+         version="9.0.21022.8" processorArchitecture="x86"
+         publicKeyToken="XXXX">
+      </assemblyIdentity>
+    </dependentAssembly>
+  </dependency>
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity type="win32" name="Microsoft.VC90.MFC"
+        version="9.0.21022.8" processorArchitecture="x86"
+        publicKeyToken="XXXX"></assemblyIdentity>
+    </dependentAssembly>
+  </dependency>
+</assembly>
+"""
+
+_CLEANED_MANIFEST = """\
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
+          manifestVersion="1.0">
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+    <security>
+      <requestedPrivileges>
+        <requestedExecutionLevel level="asInvoker" uiAccess="false">
+        </requestedExecutionLevel>
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+  <dependency>
+
+  </dependency>
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity type="win32" name="Microsoft.VC90.MFC"
+        version="9.0.21022.8" processorArchitecture="x86"
+        publicKeyToken="XXXX"></assemblyIdentity>
+    </dependentAssembly>
+  </dependency>
+</assembly>"""
+
+
+class msvc9compilerTestCase(support.TempdirManager,
+                            unittest.TestCase):
+
+    @unittest.skipUnless(sys.platform == "win32", "runs only on win32")
+    def test_no_compiler(self):
+        # make sure query_vcvarsall raises a PackagingPlatformError if
+        # the compiler is not found
+        from packaging.compiler.msvccompiler import get_build_version
+        if get_build_version() < 8.0:
+            raise unittest.SkipTest('only for MSVC8.0 or above')
+
+        from packaging.compiler import msvc9compiler
+        from packaging.compiler.msvc9compiler import query_vcvarsall
+
+        def _find_vcvarsall(version):
+            return None
+
+        old_find_vcvarsall = msvc9compiler.find_vcvarsall
+        msvc9compiler.find_vcvarsall = _find_vcvarsall
+        try:
+            self.assertRaises(PackagingPlatformError, query_vcvarsall,
+                             'wont find this version')
+        finally:
+            msvc9compiler.find_vcvarsall = old_find_vcvarsall
+
+    @unittest.skipUnless(sys.platform == "win32", "runs only on win32")
+    def test_reg_class(self):
+        from packaging.compiler.msvccompiler import get_build_version
+        if get_build_version() < 8.0:
+            raise unittest.SkipTest("requires MSVC 8.0 or later")
+
+        from packaging.compiler.msvc9compiler import Reg
+        self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx')
+
+        # looking for values that should exist on all
+        # windows registeries versions.
+        path = r'Control Panel\Desktop'
+        v = Reg.get_value(path, 'dragfullwindows')
+        self.assertIn(v, ('0', '1', '2'))
+
+        import winreg
+        HKCU = winreg.HKEY_CURRENT_USER
+        keys = Reg.read_keys(HKCU, 'xxxx')
+        self.assertEqual(keys, None)
+
+        keys = Reg.read_keys(HKCU, r'Control Panel')
+        self.assertIn('Desktop', keys)
+
+    @unittest.skipUnless(sys.platform == "win32", "runs only on win32")
+    def test_remove_visual_c_ref(self):
+        from packaging.compiler.msvccompiler import get_build_version
+        if get_build_version() < 8.0:
+            raise unittest.SkipTest("requires MSVC 8.0 or later")
+
+        from packaging.compiler.msvc9compiler import MSVCCompiler
+        tempdir = self.mkdtemp()
+        manifest = os.path.join(tempdir, 'manifest')
+        with open(manifest, 'w') as f:
+            f.write(_MANIFEST)
+
+        compiler = MSVCCompiler()
+        compiler._remove_visual_c_ref(manifest)
+
+        # see what we got
+        with open(manifest) as f:
+            # removing trailing spaces
+            content = '\n'.join(line.rstrip() for line in f.readlines())
+
+        # makes sure the manifest was properly cleaned
+        self.assertEqual(content, _CLEANED_MANIFEST)
+
+
+def test_suite():
+    return unittest.makeSuite(msvc9compilerTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_pypi_dist.py b/Lib/packaging/tests/test_pypi_dist.py
new file mode 100644
index 0000000..b438cb8
--- /dev/null
+++ b/Lib/packaging/tests/test_pypi_dist.py
@@ -0,0 +1,277 @@
+"""Tests for the packaging.pypi.dist module."""
+
+import os
+from packaging.version import VersionPredicate
+from packaging.pypi.dist import (ReleaseInfo, ReleasesList, DistInfo,
+                                 split_archive_name, get_infos_from_url)
+from packaging.pypi.errors import HashDoesNotMatch, UnsupportedHashName
+
+from packaging.tests import unittest
+from packaging.tests.support import TempdirManager
+from packaging.tests.pypi_server import use_pypi_server
+
+
+def Dist(*args, **kwargs):
+    # DistInfo takes a release as a first parameter, avoid this in tests.
+    return DistInfo(None, *args, **kwargs)
+
+
+class TestReleaseInfo(unittest.TestCase):
+
+    def test_instantiation(self):
+        # Test the DistInfo class provides us the good attributes when
+        # given on construction
+        release = ReleaseInfo("FooBar", "1.1")
+        self.assertEqual("FooBar", release.name)
+        self.assertEqual("1.1", "%s" % release.version)
+
+    def test_add_dist(self):
+        # empty distribution type should assume "sdist"
+        release = ReleaseInfo("FooBar", "1.1")
+        release.add_distribution(url="http://example.org/")
+        # should not fail
+        release['sdist']
+
+    def test_get_unknown_distribution(self):
+        # should raise a KeyError
+        pass
+
+    def test_get_infos_from_url(self):
+        # Test that the the URLs are parsed the right way
+        url_list = {
+            'FooBar-1.1.0.tar.gz': {
+                'name': 'foobar',  # lowercase the name
+                'version': '1.1.0',
+            },
+            'Foo-Bar-1.1.0.zip': {
+                'name': 'foo-bar',  # keep the dash
+                'version': '1.1.0',
+            },
+            'foobar-1.1b2.tar.gz#md5=123123123123123': {
+                'name': 'foobar',
+                'version': '1.1b2',
+                'url': 'http://example.org/foobar-1.1b2.tar.gz',  # no hash
+                'hashval': '123123123123123',
+                'hashname': 'md5',
+            },
+            'foobar-1.1-rc2.tar.gz': {  # use suggested name
+                'name': 'foobar',
+                'version': '1.1c2',
+                'url': 'http://example.org/foobar-1.1-rc2.tar.gz',
+            }
+        }
+
+        for url, attributes in url_list.items():
+            # for each url
+            infos = get_infos_from_url("http://example.org/" + url)
+            for attribute, expected in attributes.items():
+                got = infos.get(attribute)
+                if attribute == "version":
+                    self.assertEqual("%s" % got, expected)
+                else:
+                    self.assertEqual(got, expected)
+
+    def test_split_archive_name(self):
+        # Test we can split the archive names
+        names = {
+            'foo-bar-baz-1.0-rc2': ('foo-bar-baz', '1.0c2'),
+            'foo-bar-baz-1.0': ('foo-bar-baz', '1.0'),
+            'foobarbaz-1.0': ('foobarbaz', '1.0'),
+        }
+        for name, results in names.items():
+            self.assertEqual(results, split_archive_name(name))
+
+
+class TestDistInfo(TempdirManager, unittest.TestCase):
+    srcpath = "/packages/source/f/foobar/foobar-0.1.tar.gz"
+
+    def test_get_url(self):
+        # Test that the url property works well
+
+        d = Dist(url="test_url")
+        self.assertDictEqual(d.url, {
+            "url": "test_url",
+            "is_external": True,
+            "hashname": None,
+            "hashval": None,
+        })
+
+        # add a new url
+        d.add_url(url="internal_url", is_external=False)
+        self.assertEqual(d._url, None)
+        self.assertDictEqual(d.url, {
+            "url": "internal_url",
+            "is_external": False,
+            "hashname": None,
+            "hashval": None,
+        })
+        self.assertEqual(2, len(d.urls))
+
+    def test_comparison(self):
+        # Test that we can compare DistInfoributionInfoList
+        foo1 = ReleaseInfo("foo", "1.0")
+        foo2 = ReleaseInfo("foo", "2.0")
+        bar = ReleaseInfo("bar", "2.0")
+        # assert we use the version to compare
+        self.assertTrue(foo1 < foo2)
+        self.assertFalse(foo1 > foo2)
+        self.assertFalse(foo1 == foo2)
+
+        # assert we can't compare dists with different names
+        self.assertRaises(TypeError, foo1.__eq__, bar)
+
+    @use_pypi_server("downloads_with_md5")
+    def test_download(self, server):
+        # Download is possible, and the md5 is checked if given
+
+        url = server.full_address + self.srcpath
+
+        # check that a md5 if given
+        dist = Dist(url=url, hashname="md5",
+                    hashval="fe18804c5b722ff024cabdf514924fc4")
+        dist.download(self.mkdtemp())
+
+        # a wrong md5 fails
+        dist2 = Dist(url=url, hashname="md5", hashval="wrongmd5")
+
+        self.assertRaises(HashDoesNotMatch, dist2.download, self.mkdtemp())
+
+        # we can omit the md5 hash
+        dist3 = Dist(url=url)
+        dist3.download(self.mkdtemp())
+
+        # and specify a temporary location
+        # for an already downloaded dist
+        path1 = self.mkdtemp()
+        dist3.download(path=path1)
+        # and for a new one
+        path2_base = self.mkdtemp()
+        dist4 = Dist(url=url)
+        path2 = dist4.download(path=path2_base)
+        self.assertIn(path2_base, path2)
+
+    def test_hashname(self):
+        # Invalid hashnames raises an exception on assignation
+        Dist(hashname="md5", hashval="value")
+
+        self.assertRaises(UnsupportedHashName, Dist,
+                          hashname="invalid_hashname",
+                          hashval="value")
+
+    @use_pypi_server('downloads_with_md5')
+    def test_unpack(self, server):
+        url = server.full_address + self.srcpath
+        dist1 = Dist(url=url)
+
+        # unpack the distribution in a specfied folder
+        dist1_here = self.mkdtemp()
+        dist1_there = dist1.unpack(path=dist1_here)
+
+        # assert we unpack to the path provided
+        self.assertEqual(dist1_here, dist1_there)
+        dist1_result = os.listdir(dist1_there)
+        self.assertIn('paf', dist1_result)
+        os.remove(os.path.join(dist1_there, 'paf'))
+
+        # Test unpack works without a path argument
+        dist2 = Dist(url=url)
+        # doing an unpack
+        dist2_there = dist2.unpack()
+        dist2_result = os.listdir(dist2_there)
+        self.assertIn('paf', dist2_result)
+        os.remove(os.path.join(dist2_there, 'paf'))
+
+
+class TestReleasesList(unittest.TestCase):
+
+    def test_filter(self):
+        # Test we filter the distributions the right way, using version
+        # predicate match method
+        releases = ReleasesList('FooBar', (
+            ReleaseInfo("FooBar", "1.1"),
+            ReleaseInfo("FooBar", "1.1.1"),
+            ReleaseInfo("FooBar", "1.2"),
+            ReleaseInfo("FooBar", "1.2.1"),
+        ))
+        filtered = releases.filter(VersionPredicate("FooBar (<1.2)"))
+        self.assertNotIn(releases[2], filtered)
+        self.assertNotIn(releases[3], filtered)
+        self.assertIn(releases[0], filtered)
+        self.assertIn(releases[1], filtered)
+
+    def test_append(self):
+        # When adding a new item to the list, the behavior is to test if
+        # a release with the same name and version number already exists,
+        # and if so, to add a new distribution for it. If the distribution type
+        # is already defined too, add url informations to the existing DistInfo
+        # object.
+
+        releases = ReleasesList("FooBar", [
+            ReleaseInfo("FooBar", "1.1", url="external_url",
+                        dist_type="sdist"),
+        ])
+        self.assertEqual(1, len(releases))
+        releases.add_release(release=ReleaseInfo("FooBar", "1.1",
+                                                 url="internal_url",
+                                                 is_external=False,
+                                                 dist_type="sdist"))
+        self.assertEqual(1, len(releases))
+        self.assertEqual(2, len(releases[0]['sdist'].urls))
+
+        releases.add_release(release=ReleaseInfo("FooBar", "1.1.1",
+                                                 dist_type="sdist"))
+        self.assertEqual(2, len(releases))
+
+        # when adding a distribution whith a different type, a new distribution
+        # has to be added.
+        releases.add_release(release=ReleaseInfo("FooBar", "1.1.1",
+                                                 dist_type="bdist"))
+        self.assertEqual(2, len(releases))
+        self.assertEqual(2, len(releases[1].dists))
+
+    def test_prefer_final(self):
+        # Can order the distributions using prefer_final
+
+        fb10 = ReleaseInfo("FooBar", "1.0")  # final distribution
+        fb11a = ReleaseInfo("FooBar", "1.1a1")  # alpha
+        fb12a = ReleaseInfo("FooBar", "1.2a1")  # alpha
+        fb12b = ReleaseInfo("FooBar", "1.2b1")  # beta
+        dists = ReleasesList("FooBar", [fb10, fb11a, fb12a, fb12b])
+
+        dists.sort_releases(prefer_final=True)
+        self.assertEqual(fb10, dists[0])
+
+        dists.sort_releases(prefer_final=False)
+        self.assertEqual(fb12b, dists[0])
+
+#    def test_prefer_source(self):
+#        # Ordering support prefer_source
+#        fb_source = Dist("FooBar", "1.0", type="source")
+#        fb_binary = Dist("FooBar", "1.0", type="binary")
+#        fb2_binary = Dist("FooBar", "2.0", type="binary")
+#        dists = ReleasesList([fb_binary, fb_source])
+#
+#        dists.sort_distributions(prefer_source=True)
+#        self.assertEqual(fb_source, dists[0])
+#
+#        dists.sort_distributions(prefer_source=False)
+#        self.assertEqual(fb_binary, dists[0])
+#
+#        dists.append(fb2_binary)
+#        dists.sort_distributions(prefer_source=True)
+#        self.assertEqual(fb2_binary, dists[0])
+
+    def test_get_last(self):
+        dists = ReleasesList('Foo')
+        self.assertEqual(dists.get_last('Foo 1.0'), None)
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestDistInfo))
+    suite.addTest(unittest.makeSuite(TestReleaseInfo))
+    suite.addTest(unittest.makeSuite(TestReleasesList))
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_pypi_server.py b/Lib/packaging/tests/test_pypi_server.py
new file mode 100644
index 0000000..15c2e6c
--- /dev/null
+++ b/Lib/packaging/tests/test_pypi_server.py
@@ -0,0 +1,81 @@
+"""Tests for packaging.command.bdist."""
+import sys
+
+import urllib.request
+import urllib.parse
+import urllib.error
+
+from packaging.tests.pypi_server import PyPIServer, PYPI_DEFAULT_STATIC_PATH
+from packaging.tests import unittest
+
+
+class PyPIServerTest(unittest.TestCase):
+
+    def test_records_requests(self):
+        # We expect that PyPIServer can log our requests
+        server = PyPIServer()
+        server.default_response_status = 200
+
+        try:
+            server.start()
+            self.assertEqual(len(server.requests), 0)
+
+            data = b'Rock Around The Bunker'
+
+            headers = {"X-test-header": "Mister Iceberg"}
+
+            request = urllib.request.Request(server.full_address, data, headers)
+            urllib.request.urlopen(request)
+            self.assertEqual(len(server.requests), 1)
+            handler, request_data = server.requests[-1]
+            self.assertIn(data, request_data)
+            self.assertIn("x-test-header", handler.headers)
+            self.assertEqual(handler.headers["x-test-header"], "Mister Iceberg")
+
+        finally:
+            server.stop()
+
+
+    def test_serve_static_content(self):
+        # PYPI Mocked server can serve static content from disk.
+
+        def uses_local_files_for(server, url_path):
+            """Test that files are served statically (eg. the output from the
+            server is the same than the one made by a simple file read.
+            """
+            url = server.full_address + url_path
+            request = urllib.request.Request(url)
+            response = urllib.request.urlopen(request)
+            file = open(PYPI_DEFAULT_STATIC_PATH + "/test_pypi_server" +
+               url_path)
+            answer = response.read().decode() == file.read()
+            file.close()
+            return answer
+
+        server = PyPIServer(static_uri_paths=["simple", "external"],
+            static_filesystem_paths=["test_pypi_server"])
+        server.start()
+        try:
+            # the file does not exists on the disc, so it might not be served
+            url = server.full_address + "/simple/unexisting_page"
+            request = urllib.request.Request(url)
+            try:
+                urllib.request.urlopen(request)
+            except urllib.error.HTTPError as e:
+                self.assertEqual(e.code, 404)
+
+            # now try serving a content that do exists
+            self.assertTrue(uses_local_files_for(server, "/simple/index.html"))
+
+            # and another one in another root path
+            self.assertTrue(uses_local_files_for(server, "/external/index.html"))
+
+        finally:
+            server.stop()
+
+
+def test_suite():
+    return unittest.makeSuite(PyPIServerTest)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_pypi_simple.py b/Lib/packaging/tests/test_pypi_simple.py
new file mode 100644
index 0000000..c43a839
--- /dev/null
+++ b/Lib/packaging/tests/test_pypi_simple.py
@@ -0,0 +1,326 @@
+"""Tests for the packaging.pypi.simple module."""
+
+import os
+import sys
+import http.client
+import urllib.error
+import urllib.parse
+import urllib.request
+
+from packaging.pypi.simple import Crawler
+
+from packaging.tests import unittest
+from packaging.tests.support import TempdirManager, LoggingCatcher
+from packaging.tests.pypi_server import (use_pypi_server, PyPIServer,
+                                         PYPI_DEFAULT_STATIC_PATH)
+
+
+class SimpleCrawlerTestCase(TempdirManager,
+                            LoggingCatcher,
+                            unittest.TestCase):
+
+    def _get_simple_crawler(self, server, base_url="/simple/", hosts=None,
+                            *args, **kwargs):
+        """Build and return a SimpleIndex with the test server urls"""
+        if hosts is None:
+            hosts = (server.full_address.replace("http://", ""),)
+        kwargs['hosts'] = hosts
+        return Crawler(server.full_address + base_url, *args,
+                       **kwargs)
+
+    @use_pypi_server()
+    def test_bad_urls(self, server):
+        crawler = Crawler()
+        url = 'http://127.0.0.1:0/nonesuch/test_simple'
+        try:
+            v = crawler._open_url(url)
+        except Exception as v:
+            self.assertIn(url, str(v))
+        else:
+            v.close()
+            self.assertIsInstance(v, urllib.error.HTTPError)
+
+        # issue 16
+        # easy_install inquant.contentmirror.plone breaks because of a typo
+        # in its home URL
+        crawler = Crawler(hosts=('example.org',))
+        url = ('url:%20https://svn.plone.org/svn/collective/'
+               'inquant.contentmirror.plone/trunk')
+        try:
+            v = crawler._open_url(url)
+        except Exception as v:
+            self.assertIn(url, str(v))
+        else:
+            v.close()
+            self.assertIsInstance(v, urllib.error.HTTPError)
+
+        def _urlopen(*args):
+            raise http.client.BadStatusLine('line')
+
+        old_urlopen = urllib.request.urlopen
+        urllib.request.urlopen = _urlopen
+        url = 'http://example.org'
+        try:
+            v = crawler._open_url(url)
+        except Exception as v:
+            self.assertIn('line', str(v))
+        else:
+            v.close()
+            # TODO use self.assertRaises
+            raise AssertionError('Should have raise here!')
+        finally:
+            urllib.request.urlopen = old_urlopen
+
+        # issue 20
+        url = 'http://http://svn.pythonpaste.org/Paste/wphp/trunk'
+        try:
+            crawler._open_url(url)
+        except Exception as v:
+            self.assertIn('nonnumeric port', str(v))
+
+        # issue #160
+        url = server.full_address
+        page = ('<a href="http://www.famfamfam.com]('
+                'http://www.famfamfam.com/">')
+        crawler._process_url(url, page)
+
+    @use_pypi_server("test_found_links")
+    def test_found_links(self, server):
+        # Browse the index, asking for a specified release version
+        # The PyPI index contains links for version 1.0, 1.1, 2.0 and 2.0.1
+        crawler = self._get_simple_crawler(server)
+        last_release = crawler.get_release("foobar")
+
+        # we have scanned the index page
+        self.assertIn(server.full_address + "/simple/foobar/",
+            crawler._processed_urls)
+
+        # we have found 4 releases in this page
+        self.assertEqual(len(crawler._projects["foobar"]), 4)
+
+        # and returned the most recent one
+        self.assertEqual("%s" % last_release.version, '2.0.1')
+
+    def test_is_browsable(self):
+        crawler = Crawler(follow_externals=False)
+        self.assertTrue(crawler._is_browsable(crawler.index_url + "test"))
+
+        # Now, when following externals, we can have a list of hosts to trust.
+        # and don't follow other external links than the one described here.
+        crawler = Crawler(hosts=["pypi.python.org", "example.org"],
+                          follow_externals=True)
+        good_urls = (
+            "http://pypi.python.org/foo/bar",
+            "http://pypi.python.org/simple/foobar",
+            "http://example.org",
+            "http://example.org/",
+            "http://example.org/simple/",
+        )
+        bad_urls = (
+            "http://python.org",
+            "http://example.tld",
+        )
+
+        for url in good_urls:
+            self.assertTrue(crawler._is_browsable(url))
+
+        for url in bad_urls:
+            self.assertFalse(crawler._is_browsable(url))
+
+        # allow all hosts
+        crawler = Crawler(follow_externals=True, hosts=("*",))
+        self.assertTrue(crawler._is_browsable("http://an-external.link/path"))
+        self.assertTrue(crawler._is_browsable("pypi.example.org/a/path"))
+
+        # specify a list of hosts we want to allow
+        crawler = Crawler(follow_externals=True,
+                          hosts=("*.example.org",))
+        self.assertFalse(crawler._is_browsable("http://an-external.link/path"))
+        self.assertTrue(
+            crawler._is_browsable("http://pypi.example.org/a/path"))
+
+    @use_pypi_server("with_externals")
+    def test_follow_externals(self, server):
+        # Include external pages
+        # Try to request the package index, wich contains links to "externals"
+        # resources. They have to  be scanned too.
+        crawler = self._get_simple_crawler(server, follow_externals=True)
+        crawler.get_release("foobar")
+        self.assertIn(server.full_address + "/external/external.html",
+            crawler._processed_urls)
+
+    @use_pypi_server("with_real_externals")
+    def test_restrict_hosts(self, server):
+        # Only use a list of allowed hosts is possible
+        # Test that telling the simple pyPI client to not retrieve external
+        # works
+        crawler = self._get_simple_crawler(server, follow_externals=False)
+        crawler.get_release("foobar")
+        self.assertNotIn(server.full_address + "/external/external.html",
+            crawler._processed_urls)
+
+    @use_pypi_server(static_filesystem_paths=["with_externals"],
+        static_uri_paths=["simple", "external"])
+    def test_links_priority(self, server):
+        # Download links from the pypi simple index should be used before
+        # external download links.
+        # http://bitbucket.org/tarek/distribute/issue/163/md5-validation-error
+        #
+        # Usecase :
+        # - someone uploads a package on pypi, a md5 is generated
+        # - someone manually coindexes this link (with the md5 in the url) onto
+        #   an external page accessible from the package page.
+        # - someone reuploads the package (with a different md5)
+        # - while easy_installing, an MD5 error occurs because the external
+        # link is used
+        # -> The index should use the link from pypi, not the external one.
+
+        # start an index server
+        index_url = server.full_address + '/simple/'
+
+        # scan a test index
+        crawler = Crawler(index_url, follow_externals=True)
+        releases = crawler.get_releases("foobar")
+        server.stop()
+
+        # we have only one link, because links are compared without md5
+        self.assertEqual(1, len(releases))
+        self.assertEqual(1, len(releases[0].dists))
+        # the link should be from the index
+        self.assertEqual(2, len(releases[0].dists['sdist'].urls))
+        self.assertEqual('12345678901234567',
+                         releases[0].dists['sdist'].url['hashval'])
+        self.assertEqual('md5', releases[0].dists['sdist'].url['hashname'])
+
+    @use_pypi_server(static_filesystem_paths=["with_norel_links"],
+        static_uri_paths=["simple", "external"])
+    def test_not_scan_all_links(self, server):
+        # Do not follow all index page links.
+        # The links not tagged with rel="download" and rel="homepage" have
+        # to not be processed by the package index, while processing "pages".
+
+        # process the pages
+        crawler = self._get_simple_crawler(server, follow_externals=True)
+        crawler.get_releases("foobar")
+        # now it should have processed only pages with links rel="download"
+        # and rel="homepage"
+        self.assertIn("%s/simple/foobar/" % server.full_address,
+            crawler._processed_urls)  # it's the simple index page
+        self.assertIn("%s/external/homepage.html" % server.full_address,
+            crawler._processed_urls)  # the external homepage is rel="homepage"
+        self.assertNotIn("%s/external/nonrel.html" % server.full_address,
+            crawler._processed_urls)  # this link contains no rel=*
+        self.assertNotIn("%s/unrelated-0.2.tar.gz" % server.full_address,
+            crawler._processed_urls)  # linked from simple index (no rel)
+        self.assertIn("%s/foobar-0.1.tar.gz" % server.full_address,
+            crawler._processed_urls)  # linked from simple index (rel)
+        self.assertIn("%s/foobar-2.0.tar.gz" % server.full_address,
+            crawler._processed_urls)  # linked from external homepage (rel)
+
+    def test_uses_mirrors(self):
+        # When the main repository seems down, try using the given mirrors"""
+        server = PyPIServer("foo_bar_baz")
+        mirror = PyPIServer("foo_bar_baz")
+        mirror.start()  # we dont start the server here
+
+        try:
+            # create the index using both servers
+            crawler = Crawler(server.full_address + "/simple/", hosts=('*',),
+                              # set the timeout to 1s for the tests
+                              timeout=1, mirrors=[mirror.full_address])
+
+            # this should not raise a timeout
+            self.assertEqual(4, len(crawler.get_releases("foo")))
+        finally:
+            mirror.stop()
+
+    def test_simple_link_matcher(self):
+        # Test that the simple link matcher finds the right links"""
+        crawler = Crawler(follow_externals=False)
+
+        # Here, we define:
+        #   1. one link that must be followed, cause it's a download one
+        #   2. one link that must *not* be followed, cause the is_browsable
+        #      returns false for it.
+        #   3. one link that must be followed cause it's a homepage that is
+        #      browsable
+        #   4. one link that must be followed, because it contain a md5 hash
+        self.assertTrue(crawler._is_browsable("%stest" % crawler.index_url))
+        self.assertFalse(crawler._is_browsable("http://dl-link2"))
+        content = """
+        <a href="http://dl-link1" rel="download">download_link1</a>
+        <a href="http://dl-link2" rel="homepage">homepage_link1</a>
+        <a href="%(index_url)stest" rel="homepage">homepage_link2</a>
+        <a href="%(index_url)stest/foobar-1.tar.gz#md5=abcdef>download_link2</a>
+        """ % {'index_url': crawler.index_url}
+
+        # Test that the simple link matcher yield the good links.
+        generator = crawler._simple_link_matcher(content, crawler.index_url)
+        self.assertEqual(('%stest/foobar-1.tar.gz#md5=abcdef' %
+                          crawler.index_url, True), next(generator))
+        self.assertEqual(('http://dl-link1', True), next(generator))
+        self.assertEqual(('%stest' % crawler.index_url, False),
+                         next(generator))
+        self.assertRaises(StopIteration, generator.__next__)
+
+        # Follow the external links is possible (eg. homepages)
+        crawler.follow_externals = True
+        generator = crawler._simple_link_matcher(content, crawler.index_url)
+        self.assertEqual(('%stest/foobar-1.tar.gz#md5=abcdef' %
+                          crawler.index_url, True), next(generator))
+        self.assertEqual(('http://dl-link1', True), next(generator))
+        self.assertEqual(('http://dl-link2', False), next(generator))
+        self.assertEqual(('%stest' % crawler.index_url, False),
+                         next(generator))
+        self.assertRaises(StopIteration, generator.__next__)
+
+    def test_browse_local_files(self):
+        # Test that we can browse local files"""
+        index_path = os.sep.join(["file://" + PYPI_DEFAULT_STATIC_PATH,
+                                  "test_found_links", "simple"])
+        crawler = Crawler(index_path)
+        dists = crawler.get_releases("foobar")
+        self.assertEqual(4, len(dists))
+
+    def test_get_link_matcher(self):
+        crawler = Crawler("http://example.org")
+        self.assertEqual('_simple_link_matcher', crawler._get_link_matcher(
+                         "http://example.org/some/file").__name__)
+        self.assertEqual('_default_link_matcher', crawler._get_link_matcher(
+                         "http://other-url").__name__)
+
+    def test_default_link_matcher(self):
+        crawler = Crawler("http://example.org", mirrors=[])
+        crawler.follow_externals = True
+        crawler._is_browsable = lambda *args: True
+        base_url = "http://example.org/some/file/"
+        content = """
+<a href="../homepage" rel="homepage">link</a>
+<a href="../download" rel="download">link2</a>
+<a href="../simpleurl">link2</a>
+        """
+        found_links = set(uri for uri, _ in
+                          crawler._default_link_matcher(content, base_url))
+        self.assertIn('http://example.org/some/homepage', found_links)
+        self.assertIn('http://example.org/some/simpleurl', found_links)
+        self.assertIn('http://example.org/some/download', found_links)
+
+    @use_pypi_server("project_list")
+    def test_search_projects(self, server):
+        # we can search the index for some projects, on their names
+        # the case used no matters here
+        crawler = self._get_simple_crawler(server)
+        tests = (('Foobar', ['FooBar-bar', 'Foobar-baz', 'Baz-FooBar']),
+                 ('foobar*', ['FooBar-bar', 'Foobar-baz']),
+                 ('*foobar', ['Baz-FooBar']))
+
+        for search, expected in tests:
+            projects = [p.name for p in crawler.search_projects(search)]
+            self.assertListEqual(expected, projects)
+
+
+def test_suite():
+    return unittest.makeSuite(SimpleCrawlerTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_pypi_xmlrpc.py b/Lib/packaging/tests/test_pypi_xmlrpc.py
new file mode 100644
index 0000000..e27c7b3
--- /dev/null
+++ b/Lib/packaging/tests/test_pypi_xmlrpc.py
@@ -0,0 +1,93 @@
+"""Tests for the packaging.pypi.xmlrpc module."""
+
+from packaging.pypi.xmlrpc import Client, InvalidSearchField, ProjectNotFound
+
+from packaging.tests import unittest
+from packaging.tests.pypi_server import use_xmlrpc_server
+
+
+class TestXMLRPCClient(unittest.TestCase):
+    def _get_client(self, server, *args, **kwargs):
+        return Client(server.full_address, *args, **kwargs)
+
+    @use_xmlrpc_server()
+    def test_search_projects(self, server):
+        client = self._get_client(server)
+        server.xmlrpc.set_search_result(['FooBar', 'Foo', 'FooFoo'])
+        results = [r.name for r in client.search_projects(name='Foo')]
+        self.assertEqual(3, len(results))
+        self.assertIn('FooBar', results)
+        self.assertIn('Foo', results)
+        self.assertIn('FooFoo', results)
+
+    def test_search_projects_bad_fields(self):
+        client = Client()
+        self.assertRaises(InvalidSearchField, client.search_projects,
+                          invalid="test")
+
+    @use_xmlrpc_server()
+    def test_get_releases(self, server):
+        client = self._get_client(server)
+        server.xmlrpc.set_distributions([
+            {'name': 'FooBar', 'version': '1.1'},
+            {'name': 'FooBar', 'version': '1.2', 'url': 'http://some/url/'},
+            {'name': 'FooBar', 'version': '1.3', 'url': 'http://other/url/'},
+        ])
+
+        # use a lambda here to avoid an useless mock call
+        server.xmlrpc.list_releases = lambda *a, **k: ['1.1', '1.2', '1.3']
+
+        releases = client.get_releases('FooBar (<=1.2)')
+        # dont call release_data and release_url; just return name and version.
+        self.assertEqual(2, len(releases))
+        versions = releases.get_versions()
+        self.assertIn('1.1', versions)
+        self.assertIn('1.2', versions)
+        self.assertNotIn('1.3', versions)
+
+        self.assertRaises(ProjectNotFound, client.get_releases, 'Foo')
+
+    @use_xmlrpc_server()
+    def test_get_distributions(self, server):
+        client = self._get_client(server)
+        server.xmlrpc.set_distributions([
+            {'name': 'FooBar', 'version': '1.1',
+             'url': 'http://example.org/foobar-1.1-sdist.tar.gz',
+             'digest': '1234567',
+             'type': 'sdist', 'python_version': 'source'},
+            {'name':'FooBar', 'version': '1.1',
+             'url': 'http://example.org/foobar-1.1-bdist.tar.gz',
+             'digest': '8912345', 'type': 'bdist'},
+        ])
+
+        releases = client.get_releases('FooBar', '1.1')
+        client.get_distributions('FooBar', '1.1')
+        release = releases.get_release('1.1')
+        self.assertTrue('http://example.org/foobar-1.1-sdist.tar.gz',
+                        release['sdist'].url['url'])
+        self.assertTrue('http://example.org/foobar-1.1-bdist.tar.gz',
+                release['bdist'].url['url'])
+        self.assertEqual(release['sdist'].python_version, 'source')
+
+    @use_xmlrpc_server()
+    def test_get_metadata(self, server):
+        client = self._get_client(server)
+        server.xmlrpc.set_distributions([
+            {'name': 'FooBar',
+             'version': '1.1',
+             'keywords': '',
+             'obsoletes_dist': ['FooFoo'],
+             'requires_external': ['Foo'],
+            }])
+        release = client.get_metadata('FooBar', '1.1')
+        self.assertEqual(['Foo'], release.metadata['requires_external'])
+        self.assertEqual(['FooFoo'], release.metadata['obsoletes_dist'])
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestXMLRPCClient))
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_resources.py b/Lib/packaging/tests/test_resources.py
new file mode 100644
index 0000000..158af36
--- /dev/null
+++ b/Lib/packaging/tests/test_resources.py
@@ -0,0 +1,168 @@
+"""Tests for packaging.resources."""
+
+import os
+import sys
+import shutil
+import tempfile
+from textwrap import dedent
+from packaging.config import get_resources_dests
+from packaging.database import disable_cache, enable_cache
+from packaging.resources import get_file, get_file_path
+
+from packaging.tests import unittest
+from packaging.tests.test_util import GlobTestCaseBase
+
+
+class DataFilesTestCase(GlobTestCaseBase):
+
+    def assertRulesMatch(self, rules, spec):
+        tempdir = self.build_files_tree(spec)
+        expected = self.clean_tree(spec)
+        result = get_resources_dests(tempdir, rules)
+        self.assertEqual(expected, result)
+
+    def clean_tree(self, spec):
+        files = {}
+        for path, value in spec.items():
+            if value is not None:
+                path = self.os_dependent_path(path)
+                files[path] = value
+        return files
+
+    def test_simple_glob(self):
+        rules = [('', '*.tpl', '{data}')]
+        spec = {'coucou.tpl': '{data}/coucou.tpl',
+                'Donotwant': None}
+        self.assertRulesMatch(rules, spec)
+
+    def test_multiple_match(self):
+        rules = [('scripts', '*.bin', '{appdata}'),
+                 ('scripts', '*', '{appscript}')]
+        spec = {'scripts/script.bin': '{appscript}/script.bin',
+                'Babarlikestrawberry': None}
+        self.assertRulesMatch(rules, spec)
+
+    def test_set_match(self):
+        rules = [('scripts', '*.{bin,sh}', '{appscript}')]
+        spec = {'scripts/script.bin': '{appscript}/script.bin',
+                'scripts/babar.sh':  '{appscript}/babar.sh',
+                'Babarlikestrawberry': None}
+        self.assertRulesMatch(rules, spec)
+
+    def test_set_match_multiple(self):
+        rules = [('scripts', 'script{s,}.{bin,sh}', '{appscript}')]
+        spec = {'scripts/scripts.bin': '{appscript}/scripts.bin',
+                'scripts/script.sh':  '{appscript}/script.sh',
+                'Babarlikestrawberry': None}
+        self.assertRulesMatch(rules, spec)
+
+    def test_set_match_exclude(self):
+        rules = [('scripts', '*', '{appscript}'),
+                 ('', '**/*.sh', None)]
+        spec = {'scripts/scripts.bin': '{appscript}/scripts.bin',
+                'scripts/script.sh':  None,
+                'Babarlikestrawberry': None}
+        self.assertRulesMatch(rules, spec)
+
+    def test_glob_in_base(self):
+        rules = [('scrip*', '*.bin', '{appscript}')]
+        spec = {'scripts/scripts.bin': '{appscript}/scripts.bin',
+                'scripouille/babar.bin': '{appscript}/babar.bin',
+                'scriptortu/lotus.bin': '{appscript}/lotus.bin',
+                'Babarlikestrawberry': None}
+        self.assertRulesMatch(rules, spec)
+
+    def test_recursive_glob(self):
+        rules = [('', '**/*.bin', '{binary}')]
+        spec = {'binary0.bin': '{binary}/binary0.bin',
+                'scripts/binary1.bin': '{binary}/scripts/binary1.bin',
+                'scripts/bin/binary2.bin': '{binary}/scripts/bin/binary2.bin',
+                'you/kill/pandabear.guy': None}
+        self.assertRulesMatch(rules, spec)
+
+    def test_final_exemple_glob(self):
+        rules = [
+            ('mailman/database/schemas/', '*', '{appdata}/schemas'),
+            ('', '**/*.tpl', '{appdata}/templates'),
+            ('', 'developer-docs/**/*.txt', '{doc}'),
+            ('', 'README', '{doc}'),
+            ('mailman/etc/', '*', '{config}'),
+            ('mailman/foo/', '**/bar/*.cfg', '{config}/baz'),
+            ('mailman/foo/', '**/*.cfg', '{config}/hmm'),
+            ('', 'some-new-semantic.sns', '{funky-crazy-category}'),
+        ]
+        spec = {
+            'README': '{doc}/README',
+            'some.tpl': '{appdata}/templates/some.tpl',
+            'some-new-semantic.sns':
+                '{funky-crazy-category}/some-new-semantic.sns',
+            'mailman/database/mailman.db': None,
+            'mailman/database/schemas/blah.schema':
+                '{appdata}/schemas/blah.schema',
+            'mailman/etc/my.cnf': '{config}/my.cnf',
+            'mailman/foo/some/path/bar/my.cfg':
+                '{config}/hmm/some/path/bar/my.cfg',
+            'mailman/foo/some/path/other.cfg':
+                '{config}/hmm/some/path/other.cfg',
+            'developer-docs/index.txt': '{doc}/developer-docs/index.txt',
+            'developer-docs/api/toc.txt': '{doc}/developer-docs/api/toc.txt',
+        }
+        self.maxDiff = None
+        self.assertRulesMatch(rules, spec)
+
+    def test_get_file(self):
+        # Create a fake dist
+        temp_site_packages = tempfile.mkdtemp()
+        self.addCleanup(shutil.rmtree, temp_site_packages)
+
+        dist_name = 'test'
+        dist_info = os.path.join(temp_site_packages, 'test-0.1.dist-info')
+        os.mkdir(dist_info)
+
+        metadata_path = os.path.join(dist_info, 'METADATA')
+        resources_path = os.path.join(dist_info, 'RESOURCES')
+
+        with open(metadata_path, 'w') as fp:
+            fp.write(dedent("""\
+                Metadata-Version: 1.2
+                Name: test
+                Version: 0.1
+                Summary: test
+                Author: me
+                """))
+
+        test_path = 'test.cfg'
+
+        fd, test_resource_path = tempfile.mkstemp()
+        os.close(fd)
+        self.addCleanup(os.remove, test_resource_path)
+
+        with open(test_resource_path, 'w') as fp:
+            fp.write('Config')
+
+        with open(resources_path, 'w') as fp:
+            fp.write('%s,%s' % (test_path, test_resource_path))
+
+        # Add fake site-packages to sys.path to retrieve fake dist
+        self.addCleanup(sys.path.remove, temp_site_packages)
+        sys.path.insert(0, temp_site_packages)
+
+        # Force packaging.database to rescan the sys.path
+        self.addCleanup(enable_cache)
+        disable_cache()
+
+        # Try to retrieve resources paths and files
+        self.assertEqual(get_file_path(dist_name, test_path),
+                         test_resource_path)
+        self.assertRaises(KeyError, get_file_path, dist_name, 'i-dont-exist')
+
+        with get_file(dist_name, test_path) as fp:
+            self.assertEqual(fp.read(), 'Config')
+        self.assertRaises(KeyError, get_file, dist_name, 'i-dont-exist')
+
+
+def test_suite():
+    return unittest.makeSuite(DataFilesTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_run.py b/Lib/packaging/tests/test_run.py
new file mode 100644
index 0000000..01fa5aa
--- /dev/null
+++ b/Lib/packaging/tests/test_run.py
@@ -0,0 +1,62 @@
+"""Tests for packaging.run."""
+
+import os
+import sys
+import shutil
+
+from packaging.tests import unittest, support, TESTFN
+
+# setup script that uses __file__
+setup_using___file__ = """\
+
+__file__
+
+from packaging.run import setup
+setup()
+"""
+
+setup_prints_cwd = """\
+
+import os
+print os.getcwd()
+
+from packaging.run import setup
+setup()
+"""
+
+
+class CoreTestCase(unittest.TestCase):
+
+    def setUp(self):
+        super(CoreTestCase, self).setUp()
+        self.old_stdout = sys.stdout
+        self.cleanup_testfn()
+        self.old_argv = sys.argv, sys.argv[:]
+
+    def tearDown(self):
+        sys.stdout = self.old_stdout
+        self.cleanup_testfn()
+        sys.argv = self.old_argv[0]
+        sys.argv[:] = self.old_argv[1]
+        super(CoreTestCase, self).tearDown()
+
+    def cleanup_testfn(self):
+        path = TESTFN
+        if os.path.isfile(path):
+            os.remove(path)
+        elif os.path.isdir(path):
+            shutil.rmtree(path)
+
+    def write_setup(self, text, path=TESTFN):
+        with open(path, "w") as fp:
+            fp.write(text)
+        return path
+
+    # TODO restore the tests removed six months ago and port them to pysetup
+
+
+def test_suite():
+    return unittest.makeSuite(CoreTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_uninstall.py b/Lib/packaging/tests/test_uninstall.py
new file mode 100644
index 0000000..ce345b6
--- /dev/null
+++ b/Lib/packaging/tests/test_uninstall.py
@@ -0,0 +1,99 @@
+"""Tests for the uninstall command."""
+import os
+import sys
+
+from packaging.database import disable_cache, enable_cache
+from packaging.run import main
+from packaging.errors import PackagingError
+from packaging.install import remove
+from packaging.command.install_dist import install_dist
+
+from packaging.tests import unittest, support
+
+SETUP_CFG = """
+[metadata]
+name = %(name)s
+version = %(version)s
+
+[files]
+packages =
+    %(pkg)s
+    %(pkg)s.sub
+"""
+
+
+class UninstallTestCase(support.TempdirManager,
+                        support.LoggingCatcher,
+                        support.EnvironRestorer,
+                        unittest.TestCase):
+
+    restore_environ = ['PLAT']
+
+    def setUp(self):
+        super(UninstallTestCase, self).setUp()
+        self.addCleanup(setattr, sys, 'stdout', sys.stdout)
+        self.addCleanup(setattr, sys, 'stderr', sys.stderr)
+        self.addCleanup(os.chdir, os.getcwd())
+        self.addCleanup(enable_cache)
+        self.root_dir = self.mkdtemp()
+        disable_cache()
+
+    def run_setup(self, *args):
+        # run setup with args
+        args = ['run'] + list(args)
+        dist = main(args)
+        return dist
+
+    def get_path(self, dist, name):
+        cmd = install_dist(dist)
+        cmd.prefix = self.root_dir
+        cmd.finalize_options()
+        return getattr(cmd, 'install_' + name)
+
+    def make_dist(self, name='Foo', **kw):
+        kw['name'] = name
+        pkg = name.lower()
+        if 'version' not in kw:
+            kw['version'] = '0.1'
+        project_dir, dist = self.create_dist(**kw)
+        kw['pkg'] = pkg
+
+        pkg_dir = os.path.join(project_dir, pkg)
+        os.mkdir(pkg_dir)
+        os.mkdir(os.path.join(pkg_dir, 'sub'))
+
+        self.write_file((project_dir, 'setup.cfg'), SETUP_CFG % kw)
+        self.write_file((pkg_dir, '__init__.py'), '#')
+        self.write_file((pkg_dir, pkg + '_utils.py'), '#')
+        self.write_file((pkg_dir, 'sub', '__init__.py'), '#')
+        self.write_file((pkg_dir, 'sub', pkg + '_utils.py'), '#')
+
+        return project_dir
+
+    def install_dist(self, name='Foo', dirname=None, **kw):
+        if not dirname:
+            dirname = self.make_dist(name, **kw)
+        os.chdir(dirname)
+        dist = self.run_setup('install_dist', '--prefix=' + self.root_dir)
+        install_lib = self.get_path(dist, 'purelib')
+        return dist, install_lib
+
+    def test_uninstall_unknow_distribution(self):
+        self.assertRaises(PackagingError, remove, 'Foo',
+                          paths=[self.root_dir])
+
+    def test_uninstall(self):
+        dist, install_lib = self.install_dist()
+        self.assertIsFile(install_lib, 'foo', '__init__.py')
+        self.assertIsFile(install_lib, 'foo', 'sub', '__init__.py')
+        self.assertIsFile(install_lib, 'Foo-0.1.dist-info', 'RECORD')
+        remove('Foo', paths=[install_lib])
+        self.assertIsNotFile(install_lib, 'foo', 'sub', '__init__.py')
+        self.assertIsNotFile(install_lib, 'Foo-0.1.dist-info', 'RECORD')
+
+
+def test_suite():
+    return unittest.makeSuite(UninstallTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_unixccompiler.py b/Lib/packaging/tests/test_unixccompiler.py
new file mode 100644
index 0000000..16a1af3
--- /dev/null
+++ b/Lib/packaging/tests/test_unixccompiler.py
@@ -0,0 +1,132 @@
+"""Tests for packaging.unixccompiler."""
+import sys
+
+import sysconfig
+from packaging.compiler.unixccompiler import UnixCCompiler
+from packaging.tests import unittest
+
+
+class UnixCCompilerTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self._backup_platform = sys.platform
+        self._backup_get_config_var = sysconfig.get_config_var
+
+        class CompilerWrapper(UnixCCompiler):
+            def rpath_foo(self):
+                return self.runtime_library_dir_option('/foo')
+        self.cc = CompilerWrapper()
+
+    def tearDown(self):
+        sys.platform = self._backup_platform
+        sysconfig.get_config_var = self._backup_get_config_var
+
+    @unittest.skipIf(sys.platform == 'win32', 'irrelevant on win32')
+    def test_runtime_libdir_option(self):
+
+        # Issue #5900: Ensure RUNPATH is added to extension
+        # modules with RPATH if GNU ld is used
+
+        # darwin
+        sys.platform = 'darwin'
+        self.assertEqual(self.cc.rpath_foo(), '-L/foo')
+
+        # hp-ux
+        sys.platform = 'hp-ux'
+        old_gcv = sysconfig.get_config_var
+
+        def gcv(v):
+            return 'xxx'
+        sysconfig.get_config_var = gcv
+        self.assertEqual(self.cc.rpath_foo(), ['+s', '-L/foo'])
+
+        def gcv(v):
+            return 'gcc'
+        sysconfig.get_config_var = gcv
+        self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo'])
+
+        def gcv(v):
+            return 'g++'
+        sysconfig.get_config_var = gcv
+        self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo'])
+
+        sysconfig.get_config_var = old_gcv
+
+        # irix646
+        sys.platform = 'irix646'
+        self.assertEqual(self.cc.rpath_foo(), ['-rpath', '/foo'])
+
+        # osf1V5
+        sys.platform = 'osf1V5'
+        self.assertEqual(self.cc.rpath_foo(), ['-rpath', '/foo'])
+
+        # GCC GNULD
+        sys.platform = 'bar'
+
+        def gcv(v):
+            if v == 'CC':
+                return 'gcc'
+            elif v == 'GNULD':
+                return 'yes'
+        sysconfig.get_config_var = gcv
+        self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo')
+
+        # GCC non-GNULD
+        sys.platform = 'bar'
+
+        def gcv(v):
+            if v == 'CC':
+                return 'gcc'
+            elif v == 'GNULD':
+                return 'no'
+        sysconfig.get_config_var = gcv
+        self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo')
+
+        # GCC GNULD with fully qualified configuration prefix
+        # see #7617
+        sys.platform = 'bar'
+
+        def gcv(v):
+            if v == 'CC':
+                return 'x86_64-pc-linux-gnu-gcc-4.4.2'
+            elif v == 'GNULD':
+                return 'yes'
+        sysconfig.get_config_var = gcv
+        self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo')
+
+        # non-GCC GNULD
+        sys.platform = 'bar'
+
+        def gcv(v):
+            if v == 'CC':
+                return 'cc'
+            elif v == 'GNULD':
+                return 'yes'
+        sysconfig.get_config_var = gcv
+        self.assertEqual(self.cc.rpath_foo(), '-R/foo')
+
+        # non-GCC non-GNULD
+        sys.platform = 'bar'
+
+        def gcv(v):
+            if v == 'CC':
+                return 'cc'
+            elif v == 'GNULD':
+                return 'no'
+        sysconfig.get_config_var = gcv
+        self.assertEqual(self.cc.rpath_foo(), '-R/foo')
+
+        # AIX C/C++ linker
+        sys.platform = 'aix'
+
+        def gcv(v):
+            return 'xxx'
+        sysconfig.get_config_var = gcv
+        self.assertEqual(self.cc.rpath_foo(), '-blibpath:/foo')
+
+
+def test_suite():
+    return unittest.makeSuite(UnixCCompilerTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_util.py b/Lib/packaging/tests/test_util.py
new file mode 100644
index 0000000..336086d
--- /dev/null
+++ b/Lib/packaging/tests/test_util.py
@@ -0,0 +1,928 @@
+"""Tests for packaging.util."""
+import os
+import sys
+import time
+import logging
+import tempfile
+import subprocess
+from io import StringIO
+
+from packaging.tests import support, unittest
+from packaging.errors import (
+    PackagingPlatformError, PackagingByteCompileError, PackagingFileError,
+    PackagingExecError, InstallationException)
+from packaging import util
+from packaging.util import (
+    convert_path, change_root, split_quoted, strtobool, rfc822_escape,
+    get_compiler_versions, _MAC_OS_X_LD_VERSION, byte_compile, find_packages,
+    spawn, get_pypirc_path, generate_pypirc, read_pypirc, resolve_name, iglob,
+    RICH_GLOB, egginfo_to_distinfo, is_setuptools, is_distutils, is_packaging,
+    get_install_method)
+
+
+PYPIRC = """\
+[distutils]
+index-servers =
+    pypi
+    server1
+
+[pypi]
+username:me
+password:xxxx
+
+[server1]
+repository:http://example.com
+username:tarek
+password:secret
+"""
+
+PYPIRC_OLD = """\
+[server-login]
+username:tarek
+password:secret
+"""
+
+WANTED = """\
+[distutils]
+index-servers =
+    pypi
+
+[pypi]
+username:tarek
+password:xxx
+"""
+
+
+class FakePopen:
+    test_class = None
+
+    def __init__(self, cmd, shell, stdout, stderr):
+        self.cmd = cmd.split()[0]
+        exes = self.test_class._exes
+        if self.cmd not in exes:
+            # we don't want to call the system, returning an empty
+            # output so it doesn't match
+            self.stdout = StringIO()
+            self.stderr = StringIO()
+        else:
+            self.stdout = StringIO(exes[self.cmd])
+            self.stderr = StringIO()
+
+
+class UtilTestCase(support.EnvironRestorer,
+                   support.TempdirManager,
+                   support.LoggingCatcher,
+                   unittest.TestCase):
+
+    restore_environ = ['HOME']
+
+    def setUp(self):
+        super(UtilTestCase, self).setUp()
+        self.tmp_dir = self.mkdtemp()
+        self.rc = os.path.join(self.tmp_dir, '.pypirc')
+        os.environ['HOME'] = self.tmp_dir
+        # saving the environment
+        self.name = os.name
+        self.platform = sys.platform
+        self.version = sys.version
+        self.sep = os.sep
+        self.join = os.path.join
+        self.isabs = os.path.isabs
+        self.splitdrive = os.path.splitdrive
+        #self._config_vars = copy(sysconfig._config_vars)
+
+        # patching os.uname
+        if hasattr(os, 'uname'):
+            self.uname = os.uname
+            self._uname = os.uname()
+        else:
+            self.uname = None
+            self._uname = None
+        os.uname = self._get_uname
+
+        # patching POpen
+        self.old_find_executable = util.find_executable
+        util.find_executable = self._find_executable
+        self._exes = {}
+        self.old_popen = subprocess.Popen
+        self.old_stdout = sys.stdout
+        self.old_stderr = sys.stderr
+        FakePopen.test_class = self
+        subprocess.Popen = FakePopen
+
+    def tearDown(self):
+        # getting back the environment
+        os.name = self.name
+        sys.platform = self.platform
+        sys.version = self.version
+        os.sep = self.sep
+        os.path.join = self.join
+        os.path.isabs = self.isabs
+        os.path.splitdrive = self.splitdrive
+        if self.uname is not None:
+            os.uname = self.uname
+        else:
+            del os.uname
+        #sysconfig._config_vars = copy(self._config_vars)
+        util.find_executable = self.old_find_executable
+        subprocess.Popen = self.old_popen
+        sys.old_stdout = self.old_stdout
+        sys.old_stderr = self.old_stderr
+        super(UtilTestCase, self).tearDown()
+
+    def _set_uname(self, uname):
+        self._uname = uname
+
+    def _get_uname(self):
+        return self._uname
+
+    def test_convert_path(self):
+        # linux/mac
+        os.sep = '/'
+
+        def _join(path):
+            return '/'.join(path)
+        os.path.join = _join
+
+        self.assertEqual(convert_path('/home/to/my/stuff'),
+                         '/home/to/my/stuff')
+
+        # win
+        os.sep = '\\'
+
+        def _join(*path):
+            return '\\'.join(path)
+        os.path.join = _join
+
+        self.assertRaises(ValueError, convert_path, '/home/to/my/stuff')
+        self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/')
+
+        self.assertEqual(convert_path('home/to/my/stuff'),
+                         'home\\to\\my\\stuff')
+        self.assertEqual(convert_path('.'),
+                         os.curdir)
+
+    def test_change_root(self):
+        # linux/mac
+        os.name = 'posix'
+
+        def _isabs(path):
+            return path[0] == '/'
+        os.path.isabs = _isabs
+
+        def _join(*path):
+            return '/'.join(path)
+        os.path.join = _join
+
+        self.assertEqual(change_root('/root', '/old/its/here'),
+                         '/root/old/its/here')
+        self.assertEqual(change_root('/root', 'its/here'),
+                         '/root/its/here')
+
+        # windows
+        os.name = 'nt'
+
+        def _isabs(path):
+            return path.startswith('c:\\')
+        os.path.isabs = _isabs
+
+        def _splitdrive(path):
+            if path.startswith('c:'):
+                return '', path.replace('c:', '')
+            return '', path
+        os.path.splitdrive = _splitdrive
+
+        def _join(*path):
+            return '\\'.join(path)
+        os.path.join = _join
+
+        self.assertEqual(change_root('c:\\root', 'c:\\old\\its\\here'),
+                         'c:\\root\\old\\its\\here')
+        self.assertEqual(change_root('c:\\root', 'its\\here'),
+                         'c:\\root\\its\\here')
+
+        # BugsBunny os (it's a great os)
+        os.name = 'BugsBunny'
+        self.assertRaises(PackagingPlatformError,
+                          change_root, 'c:\\root', 'its\\here')
+
+        # XXX platforms to be covered: os2, mac
+
+    def test_split_quoted(self):
+        self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'),
+                         ['one', 'two', 'three', 'four'])
+
+    def test_strtobool(self):
+        yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1')
+        no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N')
+
+        for y in yes:
+            self.assertTrue(strtobool(y))
+
+        for n in no:
+            self.assertFalse(strtobool(n))
+
+    def test_rfc822_escape(self):
+        header = 'I am a\npoor\nlonesome\nheader\n'
+        res = rfc822_escape(header)
+        wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s'
+                  'header%(8s)s') % {'8s': '\n' + 8 * ' '}
+        self.assertEqual(res, wanted)
+
+    def test_find_exe_version(self):
+        # the ld version scheme under MAC OS is:
+        #   ^@(#)PROGRAM:ld  PROJECT:ld64-VERSION
+        #
+        # where VERSION is a 2-digit number for major
+        # revisions. For instance under Leopard, it's
+        # currently 77
+        #
+        # Dots are used when branching is done.
+        #
+        # The SnowLeopard ld64 is currently 95.2.12
+
+        for output, version in (('@(#)PROGRAM:ld  PROJECT:ld64-77', '77'),
+                                ('@(#)PROGRAM:ld  PROJECT:ld64-95.2.12',
+                                 '95.2.12')):
+            result = _MAC_OS_X_LD_VERSION.search(output)
+            self.assertEqual(result.group(1), version)
+
+    def _find_executable(self, name):
+        if name in self._exes:
+            return name
+        return None
+
+    def test_get_compiler_versions(self):
+        # get_versions calls distutils.spawn.find_executable on
+        # 'gcc', 'ld' and 'dllwrap'
+        self.assertEqual(get_compiler_versions(), (None, None, None))
+
+        # Let's fake we have 'gcc' and it returns '3.4.5'
+        self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF'
+        res = get_compiler_versions()
+        self.assertEqual(str(res[0]), '3.4.5')
+
+        # and let's see what happens when the version
+        # doesn't match the regular expression
+        # (\d+\.\d+(\.\d+)*)
+        self._exes['gcc'] = 'very strange output'
+        res = get_compiler_versions()
+        self.assertEqual(res[0], None)
+
+        # same thing for ld
+        if sys.platform != 'darwin':
+            self._exes['ld'] = 'GNU ld version 2.17.50 20060824'
+            res = get_compiler_versions()
+            self.assertEqual(str(res[1]), '2.17.50')
+            self._exes['ld'] = '@(#)PROGRAM:ld  PROJECT:ld64-77'
+            res = get_compiler_versions()
+            self.assertEqual(res[1], None)
+        else:
+            self._exes['ld'] = 'GNU ld version 2.17.50 20060824'
+            res = get_compiler_versions()
+            self.assertEqual(res[1], None)
+            self._exes['ld'] = '@(#)PROGRAM:ld  PROJECT:ld64-77'
+            res = get_compiler_versions()
+            self.assertEqual(str(res[1]), '77')
+
+        # and dllwrap
+        self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF'
+        res = get_compiler_versions()
+        self.assertEqual(str(res[2]), '2.17.50')
+        self._exes['dllwrap'] = 'Cheese Wrap'
+        res = get_compiler_versions()
+        self.assertEqual(res[2], None)
+
+    @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'),
+                         'sys.dont_write_bytecode not supported')
+    def test_dont_write_bytecode(self):
+        # makes sure byte_compile raise a PackagingError
+        # if sys.dont_write_bytecode is True
+        old_dont_write_bytecode = sys.dont_write_bytecode
+        sys.dont_write_bytecode = True
+        try:
+            self.assertRaises(PackagingByteCompileError, byte_compile, [])
+        finally:
+            sys.dont_write_bytecode = old_dont_write_bytecode
+
+    def test_newer(self):
+        self.assertRaises(PackagingFileError, util.newer, 'xxx', 'xxx')
+        self.newer_f1 = self.mktempfile()
+        time.sleep(1)
+        self.newer_f2 = self.mktempfile()
+        self.assertTrue(util.newer(self.newer_f2.name, self.newer_f1.name))
+
+    def test_find_packages(self):
+        # let's create a structure we want to scan:
+        #
+        #   pkg1
+        #     __init__
+        #     pkg2
+        #       __init__
+        #     pkg3
+        #       __init__
+        #       pkg6
+        #           __init__
+        #     pkg4    <--- not a pkg
+        #       pkg8
+        #          __init__
+        #   pkg5
+        #     __init__
+        #
+        root = self.mkdtemp()
+        pkg1 = os.path.join(root, 'pkg1')
+        os.mkdir(pkg1)
+        self.write_file(os.path.join(pkg1, '__init__.py'))
+        os.mkdir(os.path.join(pkg1, 'pkg2'))
+        self.write_file(os.path.join(pkg1, 'pkg2', '__init__.py'))
+        os.mkdir(os.path.join(pkg1, 'pkg3'))
+        self.write_file(os.path.join(pkg1, 'pkg3', '__init__.py'))
+        os.mkdir(os.path.join(pkg1, 'pkg3', 'pkg6'))
+        self.write_file(os.path.join(pkg1, 'pkg3', 'pkg6', '__init__.py'))
+        os.mkdir(os.path.join(pkg1, 'pkg4'))
+        os.mkdir(os.path.join(pkg1, 'pkg4', 'pkg8'))
+        self.write_file(os.path.join(pkg1, 'pkg4', 'pkg8', '__init__.py'))
+        pkg5 = os.path.join(root, 'pkg5')
+        os.mkdir(pkg5)
+        self.write_file(os.path.join(pkg5, '__init__.py'))
+
+        res = find_packages([root], ['pkg1.pkg2'])
+        self.assertEqual(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3',
+                                         'pkg1.pkg3.pkg6']))
+
+    def test_resolve_name(self):
+        self.assertIs(str, resolve_name('builtins.str'))
+        self.assertEqual(
+            UtilTestCase.__name__,
+            resolve_name("packaging.tests.test_util.UtilTestCase").__name__)
+        self.assertEqual(
+            UtilTestCase.test_resolve_name.__name__,
+            resolve_name("packaging.tests.test_util.UtilTestCase."
+                         "test_resolve_name").__name__)
+
+        self.assertRaises(ImportError, resolve_name,
+                          "packaging.tests.test_util.UtilTestCaseNot")
+        self.assertRaises(ImportError, resolve_name,
+                          "packaging.tests.test_util.UtilTestCase."
+                          "nonexistent_attribute")
+
+    def test_import_nested_first_time(self):
+        tmp_dir = self.mkdtemp()
+        os.makedirs(os.path.join(tmp_dir, 'a', 'b'))
+        self.write_file(os.path.join(tmp_dir, 'a', '__init__.py'), '')
+        self.write_file(os.path.join(tmp_dir, 'a', 'b', '__init__.py'), '')
+        self.write_file(os.path.join(tmp_dir, 'a', 'b', 'c.py'),
+                                    'class Foo: pass')
+
+        try:
+            sys.path.append(tmp_dir)
+            resolve_name("a.b.c.Foo")
+            # assert nothing raised
+        finally:
+            sys.path.remove(tmp_dir)
+
+    @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+    def test_run_2to3_on_code(self):
+        content = "print 'test'"
+        converted_content = "print('test')"
+        file_handle = self.mktempfile()
+        file_name = file_handle.name
+        file_handle.write(content)
+        file_handle.flush()
+        file_handle.seek(0)
+        from packaging.util import run_2to3
+        run_2to3([file_name])
+        new_content = "".join(file_handle.read())
+        file_handle.close()
+        self.assertEqual(new_content, converted_content)
+
+    @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+    def test_run_2to3_on_doctests(self):
+        # to check if text files containing doctests only get converted.
+        content = ">>> print 'test'\ntest\n"
+        converted_content = ">>> print('test')\ntest\n\n"
+        file_handle = self.mktempfile()
+        file_name = file_handle.name
+        file_handle.write(content)
+        file_handle.flush()
+        file_handle.seek(0)
+        from packaging.util import run_2to3
+        run_2to3([file_name], doctests_only=True)
+        new_content = "".join(file_handle.readlines())
+        file_handle.close()
+        self.assertEqual(new_content, converted_content)
+
+    @unittest.skipUnless(os.name in ('nt', 'posix'),
+                         'runs only under posix or nt')
+    def test_spawn(self):
+        # Do not patch subprocess on unix because
+        # packaging.util._spawn_posix uses it
+        if os.name in 'posix':
+            subprocess.Popen = self.old_popen
+        tmpdir = self.mkdtemp()
+
+        # creating something executable
+        # through the shell that returns 1
+        if os.name == 'posix':
+            exe = os.path.join(tmpdir, 'foo.sh')
+            self.write_file(exe, '#!/bin/sh\nexit 1')
+            os.chmod(exe, 0o777)
+        else:
+            exe = os.path.join(tmpdir, 'foo.bat')
+            self.write_file(exe, 'exit 1')
+
+        os.chmod(exe, 0o777)
+        self.assertRaises(PackagingExecError, spawn, [exe])
+
+        # now something that works
+        if os.name == 'posix':
+            exe = os.path.join(tmpdir, 'foo.sh')
+            self.write_file(exe, '#!/bin/sh\nexit 0')
+            os.chmod(exe, 0o777)
+        else:
+            exe = os.path.join(tmpdir, 'foo.bat')
+            self.write_file(exe, 'exit 0')
+
+        os.chmod(exe, 0o777)
+        spawn([exe])  # should work without any error
+
+    def test_server_registration(self):
+        # This test makes sure we know how to:
+        # 1. handle several sections in .pypirc
+        # 2. handle the old format
+
+        # new format
+        self.write_file(self.rc, PYPIRC)
+        config = read_pypirc()
+
+        config = sorted(config.items())
+        expected = [('password', 'xxxx'), ('realm', 'pypi'),
+                    ('repository', 'http://pypi.python.org/pypi'),
+                    ('server', 'pypi'), ('username', 'me')]
+        self.assertEqual(config, expected)
+
+        # old format
+        self.write_file(self.rc, PYPIRC_OLD)
+        config = read_pypirc()
+        config = sorted(config.items())
+        expected = [('password', 'secret'), ('realm', 'pypi'),
+                    ('repository', 'http://pypi.python.org/pypi'),
+                    ('server', 'server-login'), ('username', 'tarek')]
+        self.assertEqual(config, expected)
+
+    def test_server_empty_registration(self):
+        rc = get_pypirc_path()
+        self.assertFalse(os.path.exists(rc))
+        generate_pypirc('tarek', 'xxx')
+        self.assertTrue(os.path.exists(rc))
+        with open(rc) as f:
+            content = f.read()
+        self.assertEqual(content, WANTED)
+
+
+class GlobTestCaseBase(support.TempdirManager,
+                       support.LoggingCatcher,
+                       unittest.TestCase):
+
+    def build_files_tree(self, files):
+        tempdir = self.mkdtemp()
+        for filepath in files:
+            is_dir = filepath.endswith('/')
+            filepath = os.path.join(tempdir, *filepath.split('/'))
+            if is_dir:
+                dirname = filepath
+            else:
+                dirname = os.path.dirname(filepath)
+            if dirname and not os.path.exists(dirname):
+                os.makedirs(dirname)
+            if not is_dir:
+                self.write_file(filepath, 'babar')
+        return tempdir
+
+    @staticmethod
+    def os_dependent_path(path):
+        path = path.rstrip('/').split('/')
+        return os.path.join(*path)
+
+    def clean_tree(self, spec):
+        files = []
+        for path, includes in spec.items():
+            if includes:
+                files.append(self.os_dependent_path(path))
+        return files
+
+
+class GlobTestCase(GlobTestCaseBase):
+
+    def assertGlobMatch(self, glob, spec):
+        """"""
+        tempdir = self.build_files_tree(spec)
+        expected = self.clean_tree(spec)
+        self.addCleanup(os.chdir, os.getcwd())
+        os.chdir(tempdir)
+        result = list(iglob(glob))
+        self.assertCountEqual(expected, result)
+
+    def test_regex_rich_glob(self):
+        matches = RICH_GLOB.findall(
+                                r"babar aime les {fraises} est les {huitres}")
+        self.assertEqual(["fraises", "huitres"], matches)
+
+    def test_simple_glob(self):
+        glob = '*.tp?'
+        spec = {'coucou.tpl': True,
+                 'coucou.tpj': True,
+                 'Donotwant': False}
+        self.assertGlobMatch(glob, spec)
+
+    def test_simple_glob_in_dir(self):
+        glob = 'babar/*.tp?'
+        spec = {'babar/coucou.tpl': True,
+                 'babar/coucou.tpj': True,
+                 'babar/toto.bin': False,
+                 'Donotwant': False}
+        self.assertGlobMatch(glob, spec)
+
+    def test_recursive_glob_head(self):
+        glob = '**/tip/*.t?l'
+        spec = {'babar/zaza/zuzu/tip/coucou.tpl': True,
+                 'babar/z/tip/coucou.tpl': True,
+                 'babar/tip/coucou.tpl': True,
+                 'babar/zeop/tip/babar/babar.tpl': False,
+                 'babar/z/tip/coucou.bin': False,
+                 'babar/toto.bin': False,
+                 'zozo/zuzu/tip/babar.tpl': True,
+                 'zozo/tip/babar.tpl': True,
+                 'Donotwant': False}
+        self.assertGlobMatch(glob, spec)
+
+    def test_recursive_glob_tail(self):
+        glob = 'babar/**'
+        spec = {'babar/zaza/': True,
+                'babar/zaza/zuzu/': True,
+                'babar/zaza/zuzu/babar.xml': True,
+                'babar/zaza/zuzu/toto.xml': True,
+                'babar/zaza/zuzu/toto.csv': True,
+                'babar/zaza/coucou.tpl': True,
+                'babar/bubu.tpl': True,
+                'zozo/zuzu/tip/babar.tpl': False,
+                'zozo/tip/babar.tpl': False,
+                'Donotwant': False}
+        self.assertGlobMatch(glob, spec)
+
+    def test_recursive_glob_middle(self):
+        glob = 'babar/**/tip/*.t?l'
+        spec = {'babar/zaza/zuzu/tip/coucou.tpl': True,
+                 'babar/z/tip/coucou.tpl': True,
+                 'babar/tip/coucou.tpl': True,
+                 'babar/zeop/tip/babar/babar.tpl': False,
+                 'babar/z/tip/coucou.bin': False,
+                 'babar/toto.bin': False,
+                 'zozo/zuzu/tip/babar.tpl': False,
+                 'zozo/tip/babar.tpl': False,
+                 'Donotwant': False}
+        self.assertGlobMatch(glob, spec)
+
+    def test_glob_set_tail(self):
+        glob = 'bin/*.{bin,sh,exe}'
+        spec = {'bin/babar.bin': True,
+                 'bin/zephir.sh': True,
+                 'bin/celestine.exe': True,
+                 'bin/cornelius.bat': False,
+                 'bin/cornelius.xml': False,
+                 'toto/yurg': False,
+                 'Donotwant': False}
+        self.assertGlobMatch(glob, spec)
+
+    def test_glob_set_middle(self):
+        glob = 'xml/{babar,toto}.xml'
+        spec = {'xml/babar.xml': True,
+                 'xml/toto.xml': True,
+                 'xml/babar.xslt': False,
+                 'xml/cornelius.sgml': False,
+                 'xml/zephir.xml': False,
+                 'toto/yurg.xml': False,
+                 'Donotwant': False}
+        self.assertGlobMatch(glob, spec)
+
+    def test_glob_set_head(self):
+        glob = '{xml,xslt}/babar.*'
+        spec = {'xml/babar.xml': True,
+                 'xml/toto.xml': False,
+                 'xslt/babar.xslt': True,
+                 'xslt/toto.xslt': False,
+                 'toto/yurg.xml': False,
+                 'Donotwant': False}
+        self.assertGlobMatch(glob, spec)
+
+    def test_glob_all(self):
+        glob = '{xml/*,xslt/**}/babar.xml'
+        spec = {'xml/a/babar.xml': True,
+                 'xml/b/babar.xml': True,
+                 'xml/a/c/babar.xml': False,
+                 'xslt/a/babar.xml': True,
+                 'xslt/b/babar.xml': True,
+                 'xslt/a/c/babar.xml': True,
+                 'toto/yurg.xml': False,
+                 'Donotwant': False}
+        self.assertGlobMatch(glob, spec)
+
+    def test_invalid_glob_pattern(self):
+        invalids = [
+            'ppooa**',
+            'azzaeaz4**/',
+            '/**ddsfs',
+            '**##1e"&e',
+            'DSFb**c009',
+            '{',
+            '{aaQSDFa',
+            '}',
+            'aQSDFSaa}',
+            '{**a,',
+            ',**a}',
+            '{a**,',
+            ',b**}',
+            '{a**a,babar}',
+            '{bob,b**z}',
+        ]
+        msg = "%r is not supposed to be a valid pattern"
+        for pattern in invalids:
+            try:
+                iglob(pattern)
+            except ValueError:
+                continue
+            else:
+                self.fail(msg % pattern)
+
+
+class EggInfoToDistInfoTestCase(support.TempdirManager,
+                                support.LoggingCatcher,
+                                unittest.TestCase):
+
+    def get_metadata_file_paths(self, distinfo_path):
+        req_metadata_files = ['METADATA', 'RECORD', 'INSTALLER']
+        metadata_file_paths = []
+        for metadata_file in req_metadata_files:
+            path = os.path.join(distinfo_path, metadata_file)
+            metadata_file_paths.append(path)
+        return metadata_file_paths
+
+    def test_egginfo_to_distinfo_setuptools(self):
+        distinfo = 'hello-0.1.1-py3.3.dist-info'
+        egginfo = 'hello-0.1.1-py3.3.egg-info'
+        dirs = [egginfo]
+        files = ['hello.py', 'hello.pyc']
+        extra_metadata = ['dependency_links.txt', 'entry_points.txt',
+                          'not-zip-safe', 'PKG-INFO', 'top_level.txt',
+                          'SOURCES.txt']
+        for f in extra_metadata:
+            files.append(os.path.join(egginfo, f))
+
+        tempdir, record_file = self.build_dist_tree(files, dirs)
+        distinfo_path = os.path.join(tempdir, distinfo)
+        egginfo_path = os.path.join(tempdir, egginfo)
+        metadata_file_paths = self.get_metadata_file_paths(distinfo_path)
+
+        egginfo_to_distinfo(record_file)
+        # test that directories and files get created
+        self.assertTrue(os.path.isdir(distinfo_path))
+        self.assertTrue(os.path.isdir(egginfo_path))
+
+        for mfile in metadata_file_paths:
+            self.assertTrue(os.path.isfile(mfile))
+
+    def test_egginfo_to_distinfo_distutils(self):
+        distinfo = 'hello-0.1.1-py3.3.dist-info'
+        egginfo = 'hello-0.1.1-py3.3.egg-info'
+        # egginfo is a file in distutils which contains the metadata
+        files = ['hello.py', 'hello.pyc', egginfo]
+
+        tempdir, record_file = self.build_dist_tree(files, dirs=[])
+        distinfo_path = os.path.join(tempdir, distinfo)
+        egginfo_path = os.path.join(tempdir, egginfo)
+        metadata_file_paths = self.get_metadata_file_paths(distinfo_path)
+
+        egginfo_to_distinfo(record_file)
+        # test that directories and files get created
+        self.assertTrue(os.path.isdir(distinfo_path))
+        self.assertTrue(os.path.isfile(egginfo_path))
+
+        for mfile in metadata_file_paths:
+            self.assertTrue(os.path.isfile(mfile))
+
+    def build_dist_tree(self, files, dirs):
+        tempdir = self.mkdtemp()
+        record_file_path = os.path.join(tempdir, 'RECORD')
+        file_paths, dir_paths = ([], [])
+        for d in dirs:
+            path = os.path.join(tempdir, d)
+            os.makedirs(path)
+            dir_paths.append(path)
+        for f in files:
+            path = os.path.join(tempdir, f)
+            _f = open(path, 'w')
+            _f.write(f)
+            _f.close()
+            file_paths.append(path)
+
+        record_file = open(record_file_path, 'w')
+        for fpath in file_paths:
+            record_file.write(fpath + '\n')
+        for dpath in dir_paths:
+            record_file.write(dpath + '\n')
+        record_file.close()
+
+        return (tempdir, record_file_path)
+
+
+class PackagingLibChecks(support.TempdirManager,
+                         support.LoggingCatcher,
+                         unittest.TestCase):
+
+    def setUp(self):
+        super(PackagingLibChecks, self).setUp()
+        self._empty_dir = self.mkdtemp()
+
+    def test_empty_package_is_not_based_on_anything(self):
+        self.assertFalse(is_setuptools(self._empty_dir))
+        self.assertFalse(is_distutils(self._empty_dir))
+        self.assertFalse(is_packaging(self._empty_dir))
+
+    def test_setup_py_importing_setuptools_is_setuptools_based(self):
+        self.assertTrue(is_setuptools(self._setuptools_setup_py_pkg()))
+
+    def test_egg_info_dir_and_setup_py_is_setuptools_based(self):
+        self.assertTrue(is_setuptools(self._setuptools_egg_info_pkg()))
+
+    def test_egg_info_and_non_setuptools_setup_py_is_setuptools_based(self):
+        self.assertTrue(is_setuptools(self._egg_info_with_no_setuptools()))
+
+    def test_setup_py_not_importing_setuptools_is_not_setuptools_based(self):
+        self.assertFalse(is_setuptools(self._random_setup_py_pkg()))
+
+    def test_setup_py_importing_distutils_is_distutils_based(self):
+        self.assertTrue(is_distutils(self._distutils_setup_py_pkg()))
+
+    def test_pkg_info_file_and_setup_py_is_distutils_based(self):
+        self.assertTrue(is_distutils(self._distutils_pkg_info()))
+
+    def test_pkg_info_and_non_distutils_setup_py_is_distutils_based(self):
+        self.assertTrue(is_distutils(self._pkg_info_with_no_distutils()))
+
+    def test_setup_py_not_importing_distutils_is_not_distutils_based(self):
+        self.assertFalse(is_distutils(self._random_setup_py_pkg()))
+
+    def test_setup_cfg_with_no_metadata_section_is_not_packaging_based(self):
+        self.assertFalse(is_packaging(self._setup_cfg_with_no_metadata_pkg()))
+
+    def test_setup_cfg_with_valid_metadata_section_is_packaging_based(self):
+        self.assertTrue(is_packaging(self._valid_setup_cfg_pkg()))
+
+    def test_setup_cfg_and_invalid_setup_cfg_is_not_packaging_based(self):
+        self.assertFalse(is_packaging(self._invalid_setup_cfg_pkg()))
+
+    def test_get_install_method_with_setuptools_pkg(self):
+        path = self._setuptools_setup_py_pkg()
+        self.assertEqual("setuptools", get_install_method(path))
+
+    def test_get_install_method_with_distutils_pkg(self):
+        path = self._distutils_pkg_info()
+        self.assertEqual("distutils", get_install_method(path))
+
+    def test_get_install_method_with_packaging_pkg(self):
+        path = self._valid_setup_cfg_pkg()
+        self.assertEqual("packaging", get_install_method(path))
+
+    def test_get_install_method_with_unknown_pkg(self):
+        path = self._invalid_setup_cfg_pkg()
+        self.assertRaises(InstallationException, get_install_method, path)
+
+    def test_is_setuptools_logs_setup_py_text_found(self):
+        is_setuptools(self._setuptools_setup_py_pkg())
+        expected = ['setup.py file found', 'found setuptools text in setup.py']
+        self.assertEqual(expected, self.get_logs(logging.INFO))
+
+    def test_is_setuptools_logs_setup_py_text_not_found(self):
+        directory = self._random_setup_py_pkg()
+        is_setuptools(directory)
+        info_expected = ['setup.py file found']
+        warn_expected = ['no egg-info directory found',
+                         'no setuptools text found in setup.py']
+        self.assertEqual(info_expected, self.get_logs(logging.INFO))
+        self.assertEqual(warn_expected, self.get_logs(logging.WARN))
+
+    def test_is_setuptools_logs_egg_info_dir_found(self):
+        is_setuptools(self._setuptools_egg_info_pkg())
+        expected = ['setup.py file found', 'found egg-info directory']
+        self.assertEqual(expected, self.get_logs(logging.INFO))
+
+    def test_is_distutils_logs_setup_py_text_found(self):
+        is_distutils(self._distutils_setup_py_pkg())
+        expected = ['setup.py file found', 'found distutils text in setup.py']
+        self.assertEqual(expected, self.get_logs(logging.INFO))
+
+    def test_is_distutils_logs_setup_py_text_not_found(self):
+        directory = self._random_setup_py_pkg()
+        is_distutils(directory)
+        info_expected = ['setup.py file found']
+        warn_expected = ['no PKG-INFO file found',
+                         'no distutils text found in setup.py']
+        self.assertEqual(info_expected, self.get_logs(logging.INFO))
+        self.assertEqual(warn_expected, self.get_logs(logging.WARN))
+
+    def test_is_distutils_logs_pkg_info_file_found(self):
+        is_distutils(self._distutils_pkg_info())
+        expected = ['setup.py file found', 'PKG-INFO file found']
+        self.assertEqual(expected, self.get_logs(logging.INFO))
+
+    def test_is_packaging_logs_setup_cfg_found(self):
+        is_packaging(self._valid_setup_cfg_pkg())
+        expected = ['setup.cfg file found']
+        self.assertEqual(expected, self.get_logs(logging.INFO))
+
+    def test_is_packaging_logs_setup_cfg_not_found(self):
+        is_packaging(self._empty_dir)
+        expected = ['no setup.cfg file found']
+        self.assertEqual(expected, self.get_logs(logging.WARN))
+
+    def _write_setuptools_setup_py(self, directory):
+        self.write_file((directory, 'setup.py'),
+                "from setuptools import setup")
+
+    def _write_distutils_setup_py(self, directory):
+        self.write_file([directory, 'setup.py'],
+                "from distutils.core import setup")
+
+    def _write_packaging_setup_cfg(self, directory):
+        self.write_file([directory, 'setup.cfg'],
+                        ("[metadata]\n"
+                         "name = mypackage\n"
+                         "version = 0.1.0\n"))
+
+    def _setuptools_setup_py_pkg(self):
+        tmp = self.mkdtemp()
+        self._write_setuptools_setup_py(tmp)
+        return tmp
+
+    def _distutils_setup_py_pkg(self):
+        tmp = self.mkdtemp()
+        self._write_distutils_setup_py(tmp)
+        return tmp
+
+    def _valid_setup_cfg_pkg(self):
+        tmp = self.mkdtemp()
+        self._write_packaging_setup_cfg(tmp)
+        return tmp
+
+    def _setuptools_egg_info_pkg(self):
+        tmp = self.mkdtemp()
+        self._write_setuptools_setup_py(tmp)
+        tempfile.mkdtemp(suffix='.egg-info', dir=tmp)
+        return tmp
+
+    def _distutils_pkg_info(self):
+        tmp = self._distutils_setup_py_pkg()
+        self.write_file([tmp, 'PKG-INFO'], '')
+        return tmp
+
+    def _setup_cfg_with_no_metadata_pkg(self):
+        tmp = self.mkdtemp()
+        self.write_file([tmp, 'setup.cfg'],
+                        ("[othersection]\n"
+                         "foo = bar\n"))
+        return tmp
+
+    def _invalid_setup_cfg_pkg(self):
+        tmp = self.mkdtemp()
+        self.write_file([tmp, 'setup.cfg'],
+                        ("[metadata]\n"
+                         "name = john\n"
+                         "last_name = doe\n"))
+        return tmp
+
+    def _egg_info_with_no_setuptools(self):
+        tmp = self._random_setup_py_pkg()
+        tempfile.mkdtemp(suffix='.egg-info', dir=tmp)
+        return tmp
+
+    def _pkg_info_with_no_distutils(self):
+        tmp = self._random_setup_py_pkg()
+        self.write_file([tmp, 'PKG-INFO'], '')
+        return tmp
+
+    def _random_setup_py_pkg(self):
+        tmp = self.mkdtemp()
+        self.write_file((tmp, 'setup.py'), "from mypackage import setup")
+        return tmp
+
+
+def test_suite():
+    suite = unittest.makeSuite(UtilTestCase)
+    suite.addTest(unittest.makeSuite(GlobTestCase))
+    suite.addTest(unittest.makeSuite(EggInfoToDistInfoTestCase))
+    suite.addTest(unittest.makeSuite(PackagingLibChecks))
+    return suite
+
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_version.py b/Lib/packaging/tests/test_version.py
new file mode 100644
index 0000000..f94c800
--- /dev/null
+++ b/Lib/packaging/tests/test_version.py
@@ -0,0 +1,252 @@
+"""Tests for packaging.version."""
+import doctest
+import os
+
+from packaging.version import NormalizedVersion as V
+from packaging.version import HugeMajorVersionNumError, IrrationalVersionError
+from packaging.version import suggest_normalized_version as suggest
+from packaging.version import VersionPredicate
+from packaging.tests import unittest
+
+
+class VersionTestCase(unittest.TestCase):
+
+    versions = ((V('1.0'), '1.0'),
+                (V('1.1'), '1.1'),
+                (V('1.2.3'), '1.2.3'),
+                (V('1.2'), '1.2'),
+                (V('1.2.3a4'), '1.2.3a4'),
+                (V('1.2c4'), '1.2c4'),
+                (V('1.2.3.4'), '1.2.3.4'),
+                (V('1.2.3.4.0b3'), '1.2.3.4b3'),
+                (V('1.2.0.0.0'), '1.2'),
+                (V('1.0.dev345'), '1.0.dev345'),
+                (V('1.0.post456.dev623'), '1.0.post456.dev623'))
+
+    def test_repr(self):
+
+        self.assertEqual(repr(V('1.0')), "NormalizedVersion('1.0')")
+
+    def test_basic_versions(self):
+
+        for v, s in self.versions:
+            self.assertEqual(str(v), s)
+
+    def test_hash(self):
+
+        for v, s in self.versions:
+            self.assertEqual(hash(v), hash(V(s)))
+
+        versions = set([v for v, s in self.versions])
+        for v, s in self.versions:
+            self.assertIn(v, versions)
+
+        self.assertEqual(set([V('1.0')]), set([V('1.0'), V('1.0')]))
+
+    def test_from_parts(self):
+
+        for v, s in self.versions:
+            parts = v.parts
+            v2 = V.from_parts(*v.parts)
+            self.assertEqual(v, v2)
+            self.assertEqual(str(v), str(v2))
+
+    def test_irrational_versions(self):
+
+        irrational = ('1', '1.2a', '1.2.3b', '1.02', '1.2a03',
+                      '1.2a3.04', '1.2.dev.2', '1.2dev', '1.2.dev',
+                      '1.2.dev2.post2', '1.2.post2.dev3.post4')
+
+        for s in irrational:
+            self.assertRaises(IrrationalVersionError, V, s)
+
+    def test_huge_version(self):
+
+        self.assertEqual(str(V('1980.0')), '1980.0')
+        self.assertRaises(HugeMajorVersionNumError, V, '1981.0')
+        self.assertEqual(str(V('1981.0', error_on_huge_major_num=False)),
+                         '1981.0')
+
+    def test_comparison(self):
+        comparison_doctest_string = r"""
+        >>> V('1.2.0') == '1.2'
+        Traceback (most recent call last):
+        ...
+        TypeError: cannot compare NormalizedVersion and str
+
+        >>> V('1.2') < '1.3'
+        Traceback (most recent call last):
+        ...
+        TypeError: cannot compare NormalizedVersion and str
+
+        >>> V('1.2.0') == V('1.2')
+        True
+        >>> V('1.2.0') == V('1.2.3')
+        False
+        >>> V('1.2.0') != V('1.2.3')
+        True
+        >>> V('1.2.0') < V('1.2.3')
+        True
+        >>> V('1.2.0') < V('1.2.0')
+        False
+        >>> V('1.2.0') <= V('1.2.0')
+        True
+        >>> V('1.2.0') <= V('1.2.3')
+        True
+        >>> V('1.2.3') <= V('1.2.0')
+        False
+        >>> V('1.2.0') >= V('1.2.0')
+        True
+        >>> V('1.2.3') >= V('1.2.0')
+        True
+        >>> V('1.2.0') >= V('1.2.3')
+        False
+        >>> (V('1.0') > V('1.0b2'))
+        True
+        >>> (V('1.0') > V('1.0c2') > V('1.0c1') > V('1.0b2') > V('1.0b1')
+        ...  > V('1.0a2') > V('1.0a1'))
+        True
+        >>> (V('1.0.0') > V('1.0.0c2') > V('1.0.0c1') > V('1.0.0b2') > V('1.0.0b1')
+        ...  > V('1.0.0a2') > V('1.0.0a1'))
+        True
+
+        >>> V('1.0') < V('1.0.post456.dev623')
+        True
+
+        >>> V('1.0.post456.dev623') < V('1.0.post456')  < V('1.0.post1234')
+        True
+
+        >>> (V('1.0a1')
+        ...  < V('1.0a2.dev456')
+        ...  < V('1.0a2')
+        ...  < V('1.0a2.1.dev456')  # e.g. need to do a quick post release on 1.0a2
+        ...  < V('1.0a2.1')
+        ...  < V('1.0b1.dev456')
+        ...  < V('1.0b2')
+        ...  < V('1.0c1.dev456')
+        ...  < V('1.0c1')
+        ...  < V('1.0.dev7')
+        ...  < V('1.0.dev18')
+        ...  < V('1.0.dev456')
+        ...  < V('1.0.dev1234')
+        ...  < V('1.0')
+        ...  < V('1.0.post456.dev623')  # development version of a post release
+        ...  < V('1.0.post456'))
+        True
+        """
+        doctest.script_from_examples(comparison_doctest_string)
+
+    def test_suggest_normalized_version(self):
+
+        self.assertEqual(suggest('1.0'), '1.0')
+        self.assertEqual(suggest('1.0-alpha1'), '1.0a1')
+        self.assertEqual(suggest('1.0c2'), '1.0c2')
+        self.assertEqual(suggest('walla walla washington'), None)
+        self.assertEqual(suggest('2.4c1'), '2.4c1')
+        self.assertEqual(suggest('v1.0'), '1.0')
+
+        # from setuptools
+        self.assertEqual(suggest('0.4a1.r10'), '0.4a1.post10')
+        self.assertEqual(suggest('0.7a1dev-r66608'), '0.7a1.dev66608')
+        self.assertEqual(suggest('0.6a9.dev-r41475'), '0.6a9.dev41475')
+        self.assertEqual(suggest('2.4preview1'), '2.4c1')
+        self.assertEqual(suggest('2.4pre1'), '2.4c1')
+        self.assertEqual(suggest('2.1-rc2'), '2.1c2')
+
+        # from pypi
+        self.assertEqual(suggest('0.1dev'), '0.1.dev0')
+        self.assertEqual(suggest('0.1.dev'), '0.1.dev0')
+
+        # we want to be able to parse Twisted
+        # development versions are like post releases in Twisted
+        self.assertEqual(suggest('9.0.0+r2363'), '9.0.0.post2363')
+
+        # pre-releases are using markers like "pre1"
+        self.assertEqual(suggest('9.0.0pre1'), '9.0.0c1')
+
+        # we want to be able to parse Tcl-TK
+        # they us "p1" "p2" for post releases
+        self.assertEqual(suggest('1.4p1'), '1.4.post1')
+
+    def test_predicate(self):
+        # VersionPredicate knows how to parse stuff like:
+        #
+        #   Project (>=version, ver2)
+
+        predicates = ('zope.interface (>3.5.0)',
+                      'AnotherProject (3.4)',
+                      'OtherProject (<3.0)',
+                      'NoVersion',
+                      'Hey (>=2.5,<2.7)')
+
+        for predicate in predicates:
+            v = VersionPredicate(predicate)
+
+        self.assertTrue(VersionPredicate('Hey (>=2.5,<2.7)').match('2.6'))
+        self.assertTrue(VersionPredicate('Ho').match('2.6'))
+        self.assertFalse(VersionPredicate('Hey (>=2.5,!=2.6,<2.7)').match('2.6'))
+        self.assertTrue(VersionPredicate('Ho (<3.0)').match('2.6'))
+        self.assertTrue(VersionPredicate('Ho (<3.0,!=2.5)').match('2.6.0'))
+        self.assertFalse(VersionPredicate('Ho (<3.0,!=2.6)').match('2.6.0'))
+        self.assertTrue(VersionPredicate('Ho (2.5)').match('2.5.4'))
+        self.assertFalse(VersionPredicate('Ho (!=2.5)').match('2.5.2'))
+        self.assertTrue(VersionPredicate('Hey (<=2.5)').match('2.5.9'))
+        self.assertFalse(VersionPredicate('Hey (<=2.5)').match('2.6.0'))
+        self.assertTrue(VersionPredicate('Hey (>=2.5)').match('2.5.1'))
+
+        self.assertRaises(ValueError, VersionPredicate, '')
+
+        self.assertTrue(VersionPredicate('Hey 2.5').match('2.5.1'))
+
+        # XXX need to silent the micro version in this case
+        self.assertFalse(VersionPredicate('Ho (<3.0,!=2.6)').match('2.6.3'))
+
+        # Make sure a predicate that ends with a number works
+        self.assertTrue(VersionPredicate('virtualenv5 (1.0)').match('1.0'))
+        self.assertTrue(VersionPredicate('virtualenv5').match('1.0'))
+        self.assertTrue(VersionPredicate('vi5two').match('1.0'))
+        self.assertTrue(VersionPredicate('5two').match('1.0'))
+        self.assertTrue(VersionPredicate('vi5two 1.0').match('1.0'))
+        self.assertTrue(VersionPredicate('5two 1.0').match('1.0'))
+
+        # test repr
+        for predicate in predicates:
+            self.assertEqual(str(VersionPredicate(predicate)), predicate)
+
+    def test_predicate_name(self):
+        # Test that names are parsed the right way
+
+        self.assertEqual('Hey', VersionPredicate('Hey (<1.1)').name)
+        self.assertEqual('Foo-Bar', VersionPredicate('Foo-Bar (1.1)').name)
+        self.assertEqual('Foo Bar', VersionPredicate('Foo Bar (1.1)').name)
+
+    def test_is_final(self):
+        # VersionPredicate knows is a distribution is a final one or not.
+        final_versions = ('1.0', '1.0.post456')
+        other_versions = ('1.0.dev1', '1.0a2', '1.0c3')
+
+        for version in final_versions:
+            self.assertTrue(V(version).is_final)
+        for version in other_versions:
+            self.assertFalse(V(version).is_final)
+
+
+class VersionWhiteBoxTestCase(unittest.TestCase):
+
+    def test_parse_numdots(self):
+        # For code coverage completeness, as pad_zeros_length can't be set or
+        # influenced from the public interface
+        self.assertEqual(V('1.0')._parse_numdots('1.0', '1.0',
+                                                  pad_zeros_length=3),
+                          [1, 0, 0])
+
+
+def test_suite():
+    #README = os.path.join(os.path.dirname(__file__), 'README.txt')
+    #suite = [doctest.DocFileSuite(README), unittest.makeSuite(VersionTestCase)]
+    suite = [unittest.makeSuite(VersionTestCase),
+             unittest.makeSuite(VersionWhiteBoxTestCase)]
+    return unittest.TestSuite(suite)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")