Repository: conda/conda-build

Issue:
conda skeleton source
At EuroPython 2014, someone asked me if it would be possible to add a `conda skeleton source` command, where you supply the URL to the source tarball (or github repo, etc.).  This way no PyPI xmlrpc interface would be required and it would work with the `devpi` project.



Discussion:
I think somebody requested this at SciPy too.

It's not trivial, because some of the metadata is taken from PyPI. 

But isn't all the metadata in the source tarball?  I mean, you have the `setup.py`, and this has all the metadata which is used to register the package on PyPI in the first place.

It's hopefully all there. I'm just pointing out that it will require some code changes. It won't just be a one line fix. 

I might have been one of the people asking for this: 
My use case was doing 'conda skeleton pypi' against a package index hosted by devpi which doesn't implement the xmlrpc interface. So 'conda skeleton source' would make that possible, allowing to override the PyPI-URL + not using xmlrpc would solve the whole issue.


Resolving patch:
diff --git a/conda_build/pypi.py b/conda_build/pypi.py
--- a/conda_build/pypi.py
+++ b/conda_build/pypi.py
@@ -37,6 +37,8 @@
 from conda_build.build import create_env
 from conda_build.config import config
 
+from requests.packages.urllib3.util.url import parse_url
+
 PYPI_META = """\
 package:
   name: {packagename}
@@ -89,9 +91,9 @@
     # - nose
 
 about:
-  home: {homeurl}
+  {home_comment}home: {homeurl}
   license: {license}
-  summary: {summary}
+  {summary_comment}summary: {summary}
 
 # See
 # http://docs.continuum.io/conda/build.html for
@@ -126,7 +128,7 @@
 diff core.py core.py
 --- core.py
 +++ core.py
-@@ -166,5 +167,33 @@ def setup (**attrs):
+@@ -166,5 +167,39 @@ def setup (**attrs):
  \n
 +# ====== BEGIN CONDA SKELETON PYPI PATCH ======
 +
@@ -151,6 +153,12 @@
 +    data['entry_points'] = kwargs.get('entry_points', [])
 +    data['packages'] = kwargs.get('packages', [])
 +    data['setuptools'] = 'setuptools' in sys.modules
++    data['summary'] = kwargs.get('description', None)
++    data['homeurl'] = kwargs.get('url', None)
++    data['license'] = kwargs.get('license', None)
++    data['name'] = kwargs.get('name', '??PACKAGE-NAME-UNKNOWN??')
++    data['classifiers'] = kwargs.get('classifiers', None)
++    data['version'] = kwargs.get('version', '??PACKAGE-VERSION-UNKNOWN??')
 +    with io.open(os.path.join("{}", "pkginfo.yaml"), 'w', encoding='utf-8') as fn:
 +        fn.write(yaml.dump(data, encoding=None))
 +
@@ -161,6 +169,8 @@ def run_setup (script_name, script_args=None, stop_after="run"):
      """Run a setup script in a somewhat controlled environment, and
 '''
 
+INDENT = '\n    - '
+
 # https://gist.github.com/chrisguitarguy/2354951
 class RequestsTransport(Transport):
     """
@@ -232,14 +242,6 @@ def _build_url(self, host, handler):
         return '%s://%s/%s' % (scheme, host, handler)
 
 def main(args, parser):
-
-    if len(args.packages) > 1 and args.download:
-        # Because if a package's setup.py imports setuptools, it will make all
-        # future packages look like they depend on distribute. Also, who knows
-        # what kind of monkeypatching the setup.pys out there could be doing.
-        print("WARNING: building more than one recipe at once without "
-              "--no-download is not recommended")
-
     proxies = get_proxy_servers()
 
     if proxies:
@@ -249,78 +251,85 @@ def main(args, parser):
     client = ServerProxy(args.pypi_url, transport=transport)
     package_dicts = {}
     [output_dir] = args.output_dir
-    indent = '\n    - '
 
     all_packages = client.list_packages()
     all_packages_lower = [i.lower() for i in all_packages]
 
     while args.packages:
+        [output_dir] = args.output_dir
+
         package = args.packages.pop()
-        # Look for package[extra,...] features spec:
-        match_extras = re.match(r'^([^[]+)\[([^]]+)\]$', package)
-        if match_extras:
-            package, extras = match_extras.groups()
-            extras = extras.split(',')
-        else:
-            extras = []
-        dir_path = join(output_dir, package.lower())
-        if exists(dir_path):
-            raise RuntimeError("directory already exists: %s" % dir_path)
-        d = package_dicts.setdefault(package, {'packagename': package.lower(),
-                                               'run_depends': '',
-                                               'build_depends': '',
-                                               'entry_points': '',
-                                               'build_comment': '# ',
-                                               'test_commands': '',
-                                               'usemd5': '',
-                                               'entry_comment': '#',
-                                               'egg_comment': '#'})
-        d['import_tests'] = valid(package).lower()
-        if d['import_tests'] == '':
-            d['import_comment'] = '# '
-        else:
-            d['import_comment'] = ''
-            d['import_tests'] = indent + d['import_tests']
-
-        if args.version:
-            [version] = args.version
-            versions = client.package_releases(package, True)
-            if version not in versions:
-                sys.exit("Error: Version %s of %s is not available on PyPI."
-                         % (version, package))
-            d['version'] = version
+
+        is_url = ':' in package
+
+        if not is_url:
+            dir_path = join(output_dir, package.lower())
+            if exists(dir_path):
+                raise RuntimeError("directory already exists: %s" % dir_path)
+        d = package_dicts.setdefault(package,
+            {
+                'packagename': package.lower(),
+                'run_depends': '',
+                'build_depends': '',
+                'entry_points': '',
+                'build_comment': '# ',
+                'test_commands': '',
+                'usemd5': '',
+                'entry_comment': '#',
+                'egg_comment': '#',
+                'summary_comment': '',
+                'home_comment': '',
+            })
+        if is_url:
+            del d['packagename']
+
+        if is_url:
+            d['version'] = 'UNKNOWN'
         else:
-            versions = client.package_releases(package)
-            if not versions:
-                # The xmlrpc interface is case sensitive, but the index itself
-                # is apparently not (the last time I checked,
-                # len(set(all_packages_lower)) == len(set(all_packages)))
-                if package.lower() in all_packages_lower:
-                    print("%s not found, trying %s" % (package, package.capitalize()))
-                    args.packages.append(all_packages[all_packages_lower.index(package.lower())])
-                    del package_dicts[package]
-                    continue
-                sys.exit("Error: Could not find any versions of package %s" %
-                         package)
-            if len(versions) > 1:
-                print("Warning, the following versions were found for %s" %
-                      package)
-                for ver in versions:
-                    print(ver)
-                print("Using %s" % versions[0])
-                print("Use --version to specify a different version.")
-            d['version'] = versions[0]
-
-        data = client.release_data(package, d['version'])
-        urls = client.release_urls(package, d['version'])
-        if not args.all_urls:
+            if args.version:
+                [version] = args.version
+                versions = client.package_releases(package, True)
+                if version not in versions:
+                    sys.exit("Error: Version %s of %s is not available on PyPI."
+                             % (version, package))
+                d['version'] = version
+            else:
+                versions = client.package_releases(package)
+                if not versions:
+                    # The xmlrpc interface is case sensitive, but the index itself
+                    # is apparently not (the last time I checked,
+                    # len(set(all_packages_lower)) == len(set(all_packages)))
+                    if package.lower() in all_packages_lower:
+                        print("%s not found, trying %s" % (package, package.capitalize()))
+                        args.packages.append(all_packages[all_packages_lower.index(package.lower())])
+                        del package_dicts[package]
+                        continue
+                    sys.exit("Error: Could not find any versions of package %s" %
+                             package)
+                if len(versions) > 1:
+                    print("Warning, the following versions were found for %s" %
+                          package)
+                    for ver in versions:
+                        print(ver)
+                    print("Using %s" % versions[0])
+                    print("Use --version to specify a different version.")
+                d['version'] = versions[0]
+
+        data = client.release_data(package, d['version']) if not is_url else None
+        urls = client.release_urls(package, d['version']) if not is_url else [package]
+        if not is_url and not args.all_urls:
             # Try to find source urls
             urls = [url for url in urls if url['python_version'] == 'source']
         if not urls:
             if 'download_url' in data:
                 urls = [defaultdict(str, {'url': data['download_url']})]
-                urls[0]['filename'] = urls[0]['url'].split('/')[-1]
-                d['usemd5'] = '#'
+                U = parse_url(urls[0]['url'])
+                urls[0]['filename'] = U.path.rsplit('/')[-1]
+                if U.fragment.startswith('md5='):
+                    d['usemd5'] = ''
+                    d['md5'] = U.fragment[len('md5='):]
+                else:
+                    d['usemd5'] = '#'
             else:
                 sys.exit("Error: No source urls found for %s" % package)
         if len(urls) > 1 and not args.noprompt:
@@ -334,34 +343,252 @@ def main(args, parser):
         else:
             n = 0
 
-        print("Using url %s (%s) for %s." % (urls[n]['url'],
-                                             human_bytes(urls[n]['size'] or 0),
-                                             package))
+        if not is_url:
+            print("Using url %s (%s) for %s." % (urls[n]['url'],
+                human_bytes(urls[n]['size'] or 0), package))
+            d['pypiurl'] = urls[n]['url']
+            d['md5'] = urls[n]['md5_digest']
+            d['filename'] = urls[n]['filename']
+        else:
+            print("Using url %s" % package)
+            d['pypiurl'] = package
+            U = parse_url(package)
+            if U.fragment.startswith('md5='):
+                d['usemd5'] = ''
+                d['md5'] = U.fragment[len('md5='):]
+            else:
+                d['usemd5'] = '#'
+                d['md5'] = ''
+            # TODO: 'package' won't work with unpack()
+            d['filename'] = U.path.rsplit('/', 1)[-1] or 'package'
+
+        if is_url:
+            d['import_tests'] = 'PLACEHOLDER'
+        else:
+            d['import_tests'] = valid(package).lower()
+
+        get_package_metadata(args, package, d, data)
+
+        if d['import_tests'] == '':
+            d['import_comment'] = '# '
+        else:
+            d['import_comment'] = ''
+            d['import_tests'] = INDENT + d['import_tests']
+
+    for package in package_dicts:
+        d = package_dicts[package]
+        name = d['packagename']
+        makedirs(join(output_dir, name))
+        print("Writing recipe for %s" % package.lower())
+        with open(join(output_dir, name, 'meta.yaml'), 'w') as f:
+            f.write(PYPI_META.format(**d))
+        with open(join(output_dir, name, 'build.sh'), 'w') as f:
+            f.write(PYPI_BUILD_SH.format(**d))
+        with open(join(output_dir, name, 'bld.bat'), 'w') as f:
+            f.write(PYPI_BLD_BAT.format(**d))
+
+    print("Done")
+
+def get_package_metadata(args, package, d, data):
+    # Unfortunately, two important pieces of metadata are only stored in
+    # the package itself: the dependencies, and the entry points (if the
+    # package uses distribute).  Our strategy is to download the package
+    # and "fake" distribute/setuptools's setup() function to get this
+    # information from setup.py. If this sounds evil, keep in mind that
+    # distribute itself already works by monkeypatching distutils.
+
+    import yaml
+    print("Downloading %s (use --no-download to skip this step)" % package)
+    tempdir = mkdtemp('conda_skeleton_' + d['filename'])
+
+    [output_dir] = args.output_dir
+
+    if not isdir(SRC_CACHE):
+        makedirs(SRC_CACHE)
+
+    try:
+        # Download it to the build source cache. That way, you have
+        # it.
+        download_path = join(SRC_CACHE, d['filename'])
+        if not isfile(download_path) or hashsum_file(download_path,
+                                                     'md5') != d['md5']:
+            download(d['pypiurl'], join(SRC_CACHE, d['filename']))
+        else:
+            print("Using cached download")
+        print("Unpacking %s..." % package)
+        unpack(join(SRC_CACHE, d['filename']), tempdir)
+        print("done")
+        print("working in %s" % tempdir)
+        src_dir = get_dir(tempdir)
+        run_setuppy(src_dir, tempdir, args)
+        with open(join(tempdir, 'pkginfo.yaml')) as fn:
+            pkginfo = yaml.load(fn)
+
+        setuptools_build = pkginfo['setuptools']
+        setuptools_run = False
+
+        # Look at the entry_points and construct console_script and
+        #  gui_scripts entry_points for conda
+        entry_points = pkginfo['entry_points']
+        if entry_points:
+            if isinstance(entry_points, str):
+                # makes sure it is left-shifted
+                newstr = "\n".join(x.strip()
+                                   for x in entry_points.split('\n'))
+                config = configparser.ConfigParser()
+                entry_points = {}
+                try:
+                    config.readfp(StringIO(newstr))
+                except Exception as err:
+                    print("WARNING: entry-points not understood: ",
+                          err)
+                    print("The string was", newstr)
+                    entry_points = pkginfo['entry_points']
+                else:
+                    setuptools_run = True
+                    for section in config.sections():
+                        if section in ['console_scripts', 'gui_scripts']:
+                            value = ['%s=%s' % (option, config.get(section, option))
+                                     for option in config.options(section)]
+                            entry_points[section] = value
+            if not isinstance(entry_points, dict):
+                print("WARNING: Could not add entry points. They were:")
+                print(entry_points)
+            else:
+                cs = entry_points.get('console_scripts', [])
+                gs = entry_points.get('gui_scripts', [])
+                # We have *other* kinds of entry-points so we need
+                # setuptools at run-time
+                if not cs and not gs and len(entry_points) > 1:
+                    setuptools_build = True
+                    setuptools_run = True
+                entry_list = (
+                    cs
+                    # TODO: Use pythonw for these
+                    + gs)
+                if len(cs + gs) != 0:
+                    d['entry_points'] = INDENT.join([''] + entry_list)
+                    d['entry_comment'] = ''
+                    d['build_comment'] = ''
+                    d['test_commands'] = INDENT.join([''] + make_entry_tests(entry_list))
+
+        # Look for package[extra,...] features spec:
+        match_extras = re.match(r'^([^[]+)\[([^]]+)\]$', package)
+        if match_extras:
+            package, extras = match_extras.groups()
+            extras = extras.split(',')
+        else:
+            extras = []
+
+        # Extract requested extra feature requirements...
+        if args.all_extras:
+            extras_require = list(pkginfo['extras_require'].values())
+        else:
+            try:
+                extras_require = [pkginfo['extras_require'][x] for x in extras]
+            except KeyError:
+                sys.exit("Error: Invalid extra features: [%s]"
+                     % ','.join(extras))
+        #... and collect all needed requirement specs in a single list:
+        requires = []
+        for specs in [pkginfo['install_requires']] + extras_require:
+            if isinstance(specs, string_types):
+                requires.append(specs)
+            else:
+                requires.extend(specs)
+        if requires or setuptools_build or setuptools_run:
+            deps = []
+            for deptext in requires:
+                # Every item may be a single requirement
+                #  or a multiline requirements string...
+                for dep in deptext.split('\n'):
+                    #... and may also contain comments...
+                    dep = dep.split('#')[0].strip()
+                    if dep: #... and empty (or comment only) lines
+                        spec = spec_from_line(dep)
+                        if spec is None:
+                            sys.exit("Error: Could not parse: %s" % dep)
+                        deps.append(spec)
+
+            if 'setuptools' in deps:
+                setuptools_build = False
+                setuptools_run = False
+                d['egg_comment'] = ''
+                d['build_comment'] = ''
+            d['build_depends'] = INDENT.join([''] +
+                                             ['setuptools'] * setuptools_build +
+                                             deps)
+            d['run_depends'] = INDENT.join([''] +
+                                           ['setuptools'] * setuptools_run +
+                                           deps)
+
+            if args.recursive:
+                for dep in deps:
+                    dep = dep.split()[0]
+                    if not exists(join(output_dir, dep)):
+                        args.packages.append(dep)
+
+        if 'packagename' not in d:
+            d['packagename'] = pkginfo['name'].lower()
+        if d['version'] == 'UNKNOWN':
+            d['version'] = pkginfo['version']
+
+        if pkginfo['packages']:
+            deps = set(pkginfo['packages'])
+            if d['import_tests']:
+                if not d['import_tests'] or d['import_tests'] == 'PLACEHOLDER':
+                    olddeps = []
+                else:
+                    olddeps = [x for x in d['import_tests'].split()
+                           if x != '-']
+                deps = set(olddeps) | deps
+            d['import_tests'] = INDENT.join(sorted(deps))
+            d['import_comment'] = ''
 
-        d['pypiurl'] = urls[n]['url']
-        d['md5'] = urls[n]['md5_digest']
-        d['filename'] = urls[n]['filename']
+        if pkginfo['homeurl']:
+            d['homeurl'] = pkginfo['homeurl']
+        else:
+            if data:
+                d['homeurl'] = data['homeurl']
+            else:
+                d['homeurl'] = "The package home page"
+                d['home_comment'] = '#'
 
-        d['homeurl'] = data['home_page']
-        d['summary'] = repr(data['summary'])
-        license_classifier = "License :: OSI Approved ::"
-        if 'classifiers' in data:
+        if pkginfo['summary']:
+            d['summary'] = repr(pkginfo['summary'])
+        else:
+            if data:
+                d['summary'] = repr(data['summary'])
+            else:
+                d['summary'] = "Summary of the package"
+                d['summary_comment'] = '#'
+
+        license_classifier = "License :: OSI Approved :: "
+        if pkginfo['classifiers']:
+            licenses = [classifier.split(license_classifier, 1)[1] for
+                classifier in pkginfo['classifiers'] if classifier.startswith(license_classifier)]
+        elif data and 'classifiers' in data:
             licenses = [classifier.split(license_classifier, 1)[1] for classifier in
                     data['classifiers'] if classifier.startswith(license_classifier)]
         else:
             licenses = []
         if not licenses:
-            if data['license']:
+            if pkginfo['license']:
+                license = pkginfo['license']
+            elif data and 'license' in data:
+                license = data['license']
+            else:
+                license = None
+            if license:
                 if args.noprompt:
-                    license = data['license']
-                elif '\n' not in data['license']:
-                    print('Using "%s" for the license' % data['license'])
-                    license = data['license']
+                    pass
+                elif '\n' not in license:
+                    print('Using "%s" for the license' % license)
                 else:
                     # Some projects put the whole license text in this field
                     print("This is the license for %s" % package)
                     print()
-                    print(data['license'])
+                    print(license)
                     print()
                     license = input("What license string should I use? ")
             else:
@@ -369,164 +596,14 @@ def main(args, parser):
                     license = "UNKNOWN"
                 else:
                     license = input(("No license could be found for %s on " +
-                                     "PyPI. What license should I use? ") %
+                                     "PyPI or in the source. What license should I use? ") %
                                     package)
         else:
             license = ' or '.join(licenses)
         d['license'] = license
 
-        # Unfortunately, two important pieces of metadata are only stored in
-        # the package itself: the dependencies, and the entry points (if the
-        # package uses distribute).  Our strategy is to download the package
-        # and "fake" distribute/setuptools's setup() function to get this
-        # information from setup.py. If this sounds evil, keep in mind that
-        # distribute itself already works by monkeypatching distutils.
-        if args.download:
-            import yaml
-            print("Downloading %s (use --no-download to skip this step)" %
-                  package)
-            tempdir = mkdtemp('conda_skeleton_' + package)
-
-            if not isdir(SRC_CACHE):
-                makedirs(SRC_CACHE)
-
-            try:
-                # Download it to the build source cache. That way, you have
-                # it.
-                download_path = join(SRC_CACHE, d['filename'])
-                if not isfile(download_path) or hashsum_file(download_path,
-                                                             'md5') != d['md5']:
-                    download(d['pypiurl'], join(SRC_CACHE, d['filename']))
-                else:
-                    print("Using cached download")
-                print("Unpacking %s..." % package)
-                unpack(join(SRC_CACHE, d['filename']), tempdir)
-                print("done")
-                print("working in %s" % tempdir)
-                src_dir = get_dir(tempdir)
-                run_setuppy(src_dir, tempdir, args)
-                with open(join(tempdir, 'pkginfo.yaml')) as fn:
-                    pkginfo = yaml.load(fn)
-
-                setuptools_build = pkginfo['setuptools']
-                setuptools_run = False
-
-                # Look at the entry_points and construct console_script and
-                #  gui_scripts entry_points for conda
-                entry_points = pkginfo['entry_points']
-                if entry_points:
-                    if isinstance(entry_points, str):
-                        # makes sure it is left-shifted
-                        newstr = "\n".join(x.strip()
-                                           for x in entry_points.split('\n'))
-                        config = configparser.ConfigParser()
-                        entry_points = {}
-                        try:
-                            config.readfp(StringIO(newstr))
-                        except Exception as err:
-                            print("WARNING: entry-points not understood: ",
-                                  err)
-                            print("The string was", newstr)
-                            entry_points = pkginfo['entry_points']
-                        else:
-                            setuptools_run = True
-                            for section in config.sections():
-                                if section in ['console_scripts', 'gui_scripts']:
-                                    value = ['%s=%s' % (option, config.get(section, option))
-                                             for option in config.options(section)]
-                                    entry_points[section] = value
-                    if not isinstance(entry_points, dict):
-                        print("WARNING: Could not add entry points. They were:")
-                        print(entry_points)
-                    else:
-                        cs = entry_points.get('console_scripts', [])
-                        gs = entry_points.get('gui_scripts', [])
-                        # We have *other* kinds of entry-points so we need
-                        # setuptools at run-time
-                        if not cs and not gs and len(entry_points) > 1:
-                            setuptools_build = True
-                            setuptools_run = True
-                        entry_list = (
-                            cs
-                            # TODO: Use pythonw for these
-                            + gs)
-                        if len(cs + gs) != 0:
-                            d['entry_points'] = indent.join([''] + entry_list)
-                            d['entry_comment'] = ''
-                            d['build_comment'] = ''
-                            d['test_commands'] = indent.join([''] + make_entry_tests(entry_list))
-
-                # Extract requested extra feature requirements...
-                if args.all_extras:
-                    extras_require = list(pkginfo['extras_require'].values())
-                else:
-                    try:
-                        extras_require = [pkginfo['extras_require'][x] for x in extras]
-                    except KeyError:
-                        sys.exit("Error: Invalid extra features: [%s]"
-                             % ','.join(extras))
-                #... and collect all needed requirement specs in a single list:
-                requires = []
-                for specs in [pkginfo['install_requires']] + extras_require:
-                    if isinstance(specs, string_types):
-                        requires.append(specs)
-                    else:
-                        requires.extend(specs)
-                if requires or setuptools_build or setuptools_run:
-                    deps = []
-                    for deptext in requires:
-                        # Every item may be a single requirement
-                        #  or a multiline requirements string...
-                        for dep in deptext.split('\n'):
-                            #... and may also contain comments...
-                            dep = dep.split('#')[0].strip()
-                            if dep: #... and empty (or comment only) lines
-                                spec = spec_from_line(dep)
-                                if spec is None:
-                                    sys.exit("Error: Could not parse: %s" % dep)
-                                deps.append(spec)
-
-                    if 'setuptools' in deps:
-                        setuptools_build = False
-                        setuptools_run = False
-                        d['egg_comment'] = ''
-                        d['build_comment'] = ''
-                    d['build_depends'] = indent.join([''] +
-                                                     ['setuptools'] * setuptools_build +
-                                                     deps)
-                    d['run_depends'] = indent.join([''] +
-                                                   ['setuptools'] * setuptools_run +
-                                                   deps)
-
-                    if args.recursive:
-                        for dep in deps:
-                            dep = dep.split()[0]
-                            if not exists(join(output_dir, dep)):
-                                args.packages.append(dep)
-
-                if pkginfo['packages']:
-                    deps = set(pkginfo['packages'])
-                    if d['import_tests']:
-                        olddeps = [x for x in d['import_tests'].split()
-                                   if x != '-']
-                        deps = set(olddeps) | deps
-                    d['import_tests'] = indent.join([''] + sorted(deps))
-                    d['import_comment'] = ''
-            finally:
-                rm_rf(tempdir)
-
-    for package in package_dicts:
-        d = package_dicts[package]
-        makedirs(join(output_dir, package.lower()))
-        print("Writing recipe for %s" % package.lower())
-        with open(join(output_dir, package.lower(), 'meta.yaml'), 'w') as f:
-            f.write(PYPI_META.format(**d))
-        with open(join(output_dir, package.lower(), 'build.sh'), 'w') as f:
-            f.write(PYPI_BUILD_SH.format(**d))
-        with open(join(output_dir, package.lower(), 'bld.bat'), 'w') as f:
-            f.write(PYPI_BLD_BAT.format(**d))
-
-    print("Done")
+    finally:
+        rm_rf(tempdir)
 
 
 def valid(name):


Test patch:
diff --git a/test-recipes/TODO.md b/tests/test-recipes/TODO.md
similarity index 100%
rename from test-recipes/TODO.md
rename to tests/test-recipes/TODO.md
diff --git a/test-recipes/build_recipes.sh b/tests/test-recipes/build_recipes.sh
similarity index 100%
rename from test-recipes/build_recipes.sh
rename to tests/test-recipes/build_recipes.sh
diff --git a/test-recipes/metadata/TODO.md b/tests/test-recipes/metadata/TODO.md
similarity index 100%
rename from test-recipes/metadata/TODO.md
rename to tests/test-recipes/metadata/TODO.md
diff --git a/test-recipes/metadata/binary_has_prefix_files/build.sh b/tests/test-recipes/metadata/binary_has_prefix_files/build.sh
similarity index 100%
rename from test-recipes/metadata/binary_has_prefix_files/build.sh
rename to tests/test-recipes/metadata/binary_has_prefix_files/build.sh
diff --git a/test-recipes/metadata/binary_has_prefix_files/meta.yaml b/tests/test-recipes/metadata/binary_has_prefix_files/meta.yaml
similarity index 100%
rename from test-recipes/metadata/binary_has_prefix_files/meta.yaml
rename to tests/test-recipes/metadata/binary_has_prefix_files/meta.yaml
diff --git a/test-recipes/metadata/binary_has_prefix_files/run_test.sh b/tests/test-recipes/metadata/binary_has_prefix_files/run_test.sh
similarity index 100%
rename from test-recipes/metadata/binary_has_prefix_files/run_test.sh
rename to tests/test-recipes/metadata/binary_has_prefix_files/run_test.sh
diff --git a/test-recipes/metadata/binary_has_prefix_files/write_binary_has_prefix.py b/tests/test-recipes/metadata/binary_has_prefix_files/write_binary_has_prefix.py
similarity index 100%
rename from test-recipes/metadata/binary_has_prefix_files/write_binary_has_prefix.py
rename to tests/test-recipes/metadata/binary_has_prefix_files/write_binary_has_prefix.py
diff --git a/test-recipes/metadata/build_number/meta.yaml b/tests/test-recipes/metadata/build_number/meta.yaml
similarity index 100%
rename from test-recipes/metadata/build_number/meta.yaml
rename to tests/test-recipes/metadata/build_number/meta.yaml
diff --git a/test-recipes/metadata/build_number/run_test.bat b/tests/test-recipes/metadata/build_number/run_test.bat
similarity index 100%
rename from test-recipes/metadata/build_number/run_test.bat
rename to tests/test-recipes/metadata/build_number/run_test.bat
diff --git a/test-recipes/metadata/build_number/run_test.sh b/tests/test-recipes/metadata/build_number/run_test.sh
similarity index 100%
rename from test-recipes/metadata/build_number/run_test.sh
rename to tests/test-recipes/metadata/build_number/run_test.sh
diff --git a/test-recipes/metadata/build_string/meta.yaml b/tests/test-recipes/metadata/build_string/meta.yaml
similarity index 100%
rename from test-recipes/metadata/build_string/meta.yaml
rename to tests/test-recipes/metadata/build_string/meta.yaml
diff --git a/test-recipes/metadata/build_string/run_test.bat b/tests/test-recipes/metadata/build_string/run_test.bat
similarity index 100%
rename from test-recipes/metadata/build_string/run_test.bat
rename to tests/test-recipes/metadata/build_string/run_test.bat
diff --git a/test-recipes/metadata/build_string/run_test.sh b/tests/test-recipes/metadata/build_string/run_test.sh
similarity index 100%
rename from test-recipes/metadata/build_string/run_test.sh
rename to tests/test-recipes/metadata/build_string/run_test.sh
diff --git a/test-recipes/metadata/detect_binary_files_with_prefix/build.sh b/tests/test-recipes/metadata/detect_binary_files_with_prefix/build.sh
similarity index 100%
rename from test-recipes/metadata/detect_binary_files_with_prefix/build.sh
rename to tests/test-recipes/metadata/detect_binary_files_with_prefix/build.sh
diff --git a/test-recipes/metadata/detect_binary_files_with_prefix/meta.yaml b/tests/test-recipes/metadata/detect_binary_files_with_prefix/meta.yaml
similarity index 100%
rename from test-recipes/metadata/detect_binary_files_with_prefix/meta.yaml
rename to tests/test-recipes/metadata/detect_binary_files_with_prefix/meta.yaml
diff --git a/test-recipes/metadata/detect_binary_files_with_prefix/run_test.sh b/tests/test-recipes/metadata/detect_binary_files_with_prefix/run_test.sh
similarity index 100%
rename from test-recipes/metadata/detect_binary_files_with_prefix/run_test.sh
rename to tests/test-recipes/metadata/detect_binary_files_with_prefix/run_test.sh
diff --git a/test-recipes/metadata/detect_binary_files_with_prefix/write_binary_has_prefix.py b/tests/test-recipes/metadata/detect_binary_files_with_prefix/write_binary_has_prefix.py
similarity index 100%
rename from test-recipes/metadata/detect_binary_files_with_prefix/write_binary_has_prefix.py
rename to tests/test-recipes/metadata/detect_binary_files_with_prefix/write_binary_has_prefix.py
diff --git a/test-recipes/metadata/entry_points/bld.bat b/tests/test-recipes/metadata/entry_points/bld.bat
similarity index 57%
rename from test-recipes/metadata/entry_points/bld.bat
rename to tests/test-recipes/metadata/entry_points/bld.bat
--- a/test-recipes/metadata/entry_points/bld.bat
+++ b/tests/test-recipes/metadata/entry_points/bld.bat
@@ -1,3 +1,3 @@
-cd test-recipes\test-package
+cd tests\test-recipes\test-package
 python setup.py install
 if errorlevel 1 exit 1
diff --git a/tests/test-recipes/metadata/entry_points/build.sh b/tests/test-recipes/metadata/entry_points/build.sh
new file mode 100644
--- /dev/null
+++ b/tests/test-recipes/metadata/entry_points/build.sh
@@ -0,0 +1,3 @@
+cd tests/test-recipes/test-package
+
+python setup.py install
diff --git a/test-recipes/metadata/entry_points/meta.yaml b/tests/test-recipes/metadata/entry_points/meta.yaml
similarity index 89%
rename from test-recipes/metadata/entry_points/meta.yaml
rename to tests/test-recipes/metadata/entry_points/meta.yaml
--- a/test-recipes/metadata/entry_points/meta.yaml
+++ b/tests/test-recipes/metadata/entry_points/meta.yaml
@@ -3,7 +3,7 @@ package:
   version: 1.0
 
 source:
-  git_url: ../../../
+  git_url: ../../../../
 
 build:
   entry_points:
diff --git a/test-recipes/metadata/entry_points/run_test.bat b/tests/test-recipes/metadata/entry_points/run_test.bat
similarity index 100%
rename from test-recipes/metadata/entry_points/run_test.bat
rename to tests/test-recipes/metadata/entry_points/run_test.bat
diff --git a/test-recipes/metadata/entry_points/run_test.sh b/tests/test-recipes/metadata/entry_points/run_test.sh
similarity index 100%
rename from test-recipes/metadata/entry_points/run_test.sh
rename to tests/test-recipes/metadata/entry_points/run_test.sh
diff --git a/test-recipes/metadata/has_prefix_files/build.sh b/tests/test-recipes/metadata/has_prefix_files/build.sh
similarity index 100%
rename from test-recipes/metadata/has_prefix_files/build.sh
rename to tests/test-recipes/metadata/has_prefix_files/build.sh
diff --git a/test-recipes/metadata/has_prefix_files/meta.yaml b/tests/test-recipes/metadata/has_prefix_files/meta.yaml
similarity index 100%
rename from test-recipes/metadata/has_prefix_files/meta.yaml
rename to tests/test-recipes/metadata/has_prefix_files/meta.yaml
diff --git a/test-recipes/metadata/has_prefix_files/run_test.sh b/tests/test-recipes/metadata/has_prefix_files/run_test.sh
similarity index 100%
rename from test-recipes/metadata/has_prefix_files/run_test.sh
rename to tests/test-recipes/metadata/has_prefix_files/run_test.sh
diff --git a/test-recipes/metadata/has_prefix_files/write_binary_has_prefix.py b/tests/test-recipes/metadata/has_prefix_files/write_binary_has_prefix.py
similarity index 100%
rename from test-recipes/metadata/has_prefix_files/write_binary_has_prefix.py
rename to tests/test-recipes/metadata/has_prefix_files/write_binary_has_prefix.py
diff --git a/tests/test-recipes/metadata/osx_is_app/build.sh b/tests/test-recipes/metadata/osx_is_app/build.sh
new file mode 100644
--- /dev/null
+++ b/tests/test-recipes/metadata/osx_is_app/build.sh
@@ -0,0 +1,3 @@
+cd tests/test-recipes/test-package
+
+python setup.py install
diff --git a/test-recipes/metadata/osx_is_app/meta.yaml b/tests/test-recipes/metadata/osx_is_app/meta.yaml
similarity index 91%
rename from test-recipes/metadata/osx_is_app/meta.yaml
rename to tests/test-recipes/metadata/osx_is_app/meta.yaml
--- a/test-recipes/metadata/osx_is_app/meta.yaml
+++ b/tests/test-recipes/metadata/osx_is_app/meta.yaml
@@ -3,7 +3,7 @@ package:
   version: 1.0
 
 source:
-  git_url: ../../../
+  git_url: ../../../../
 
 build:
   entry_points:
diff --git a/test-recipes/metadata/osx_is_app/run_test.sh b/tests/test-recipes/metadata/osx_is_app/run_test.sh
similarity index 100%
rename from test-recipes/metadata/osx_is_app/run_test.sh
rename to tests/test-recipes/metadata/osx_is_app/run_test.sh
diff --git a/test-recipes/metadata/source_git/bld.bat b/tests/test-recipes/metadata/source_git/bld.bat
similarity index 100%
rename from test-recipes/metadata/source_git/bld.bat
rename to tests/test-recipes/metadata/source_git/bld.bat
diff --git a/test-recipes/metadata/source_git/build.sh b/tests/test-recipes/metadata/source_git/build.sh
similarity index 100%
rename from test-recipes/metadata/source_git/build.sh
rename to tests/test-recipes/metadata/source_git/build.sh
diff --git a/test-recipes/metadata/source_git/meta.yaml b/tests/test-recipes/metadata/source_git/meta.yaml
similarity index 100%
rename from test-recipes/metadata/source_git/meta.yaml
rename to tests/test-recipes/metadata/source_git/meta.yaml
diff --git a/test-recipes/metadata/source_hg/bld.bat b/tests/test-recipes/metadata/source_hg/bld.bat
similarity index 100%
rename from test-recipes/metadata/source_hg/bld.bat
rename to tests/test-recipes/metadata/source_hg/bld.bat
diff --git a/test-recipes/metadata/source_hg/build.sh b/tests/test-recipes/metadata/source_hg/build.sh
similarity index 100%
rename from test-recipes/metadata/source_hg/build.sh
rename to tests/test-recipes/metadata/source_hg/build.sh
diff --git a/test-recipes/metadata/source_hg/meta.yaml b/tests/test-recipes/metadata/source_hg/meta.yaml
similarity index 100%
rename from test-recipes/metadata/source_hg/meta.yaml
rename to tests/test-recipes/metadata/source_hg/meta.yaml
diff --git a/test-recipes/metadata/source_svn/bld.bat b/tests/test-recipes/metadata/source_svn/bld.bat
similarity index 100%
rename from test-recipes/metadata/source_svn/bld.bat
rename to tests/test-recipes/metadata/source_svn/bld.bat
diff --git a/test-recipes/metadata/source_svn/build.sh b/tests/test-recipes/metadata/source_svn/build.sh
similarity index 100%
rename from test-recipes/metadata/source_svn/build.sh
rename to tests/test-recipes/metadata/source_svn/build.sh
diff --git a/test-recipes/metadata/source_svn/meta.yaml b/tests/test-recipes/metadata/source_svn/meta.yaml
similarity index 100%
rename from test-recipes/metadata/source_svn/meta.yaml
rename to tests/test-recipes/metadata/source_svn/meta.yaml
diff --git a/test-recipes/metadata/source_url/bld.bat b/tests/test-recipes/metadata/source_url/bld.bat
similarity index 100%
rename from test-recipes/metadata/source_url/bld.bat
rename to tests/test-recipes/metadata/source_url/bld.bat
diff --git a/test-recipes/metadata/source_url/build.sh b/tests/test-recipes/metadata/source_url/build.sh
similarity index 100%
rename from test-recipes/metadata/source_url/build.sh
rename to tests/test-recipes/metadata/source_url/build.sh
diff --git a/test-recipes/metadata/source_url/meta.yaml b/tests/test-recipes/metadata/source_url/meta.yaml
similarity index 100%
rename from test-recipes/metadata/source_url/meta.yaml
rename to tests/test-recipes/metadata/source_url/meta.yaml
diff --git a/test-recipes/test-package/bin/test-script-setup.py b/tests/test-recipes/test-package/bin/test-script-setup.py
similarity index 100%
rename from test-recipes/test-package/bin/test-script-setup.py
rename to tests/test-recipes/test-package/bin/test-script-setup.py
diff --git a/test-recipes/test-package/conda_build_test/__init__.py b/tests/test-recipes/test-package/conda_build_test/__init__.py
similarity index 100%
rename from test-recipes/test-package/conda_build_test/__init__.py
rename to tests/test-recipes/test-package/conda_build_test/__init__.py
diff --git a/test-recipes/test-package/conda_build_test/manual_entry.py b/tests/test-recipes/test-package/conda_build_test/manual_entry.py
similarity index 100%
rename from test-recipes/test-package/conda_build_test/manual_entry.py
rename to tests/test-recipes/test-package/conda_build_test/manual_entry.py
diff --git a/test-recipes/test-package/setup.py b/tests/test-recipes/test-package/setup.py
similarity index 100%
rename from test-recipes/test-package/setup.py
rename to tests/test-recipes/test-package/setup.py
diff --git a/tests/test-skeleton/sympy-0.7.5-url/bld.bat b/tests/test-skeleton/sympy-0.7.5-url/bld.bat
new file mode 100644
--- /dev/null
+++ b/tests/test-skeleton/sympy-0.7.5-url/bld.bat
@@ -0,0 +1,8 @@
+"%PYTHON%" setup.py install
+if errorlevel 1 exit 1
+
+:: Add more build steps here, if they are necessary.
+
+:: See
+:: http://docs.continuum.io/conda/build.html
+:: for a list of environment variables that are set during the build process.
diff --git a/tests/test-skeleton/sympy-0.7.5-url/build.sh b/tests/test-skeleton/sympy-0.7.5-url/build.sh
new file mode 100644
--- /dev/null
+++ b/tests/test-skeleton/sympy-0.7.5-url/build.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+$PYTHON setup.py install
+
+# Add more build steps here, if they are necessary.
+
+# See
+# http://docs.continuum.io/conda/build.html
+# for a list of environment variables that are set during the build process.
diff --git a/tests/test-skeleton/sympy-0.7.5-url/meta.yaml b/tests/test-skeleton/sympy-0.7.5-url/meta.yaml
new file mode 100644
--- /dev/null
+++ b/tests/test-skeleton/sympy-0.7.5-url/meta.yaml
@@ -0,0 +1,162 @@
+package:
+  name: sympy
+  version: !!str 0.7.5
+
+source:
+  fn: sympy-0.7.5.tar.gz
+  url: https://pypi.python.org/packages/source/s/sympy/sympy-0.7.5.tar.gz#md5=7de1adb49972a15a3dd975e879a2bea9
+  md5: 7de1adb49972a15a3dd975e879a2bea9
+#  patches:
+   # List any patch files here
+   # - fix.patch
+
+# build:
+  #preserve_egg_dir: True
+  #entry_points:
+    # Put any entry points (scripts to be generated automatically) here. The
+    # syntax is module:function.  For example
+    #
+    # - sympy = sympy:main
+    #
+    # Would create an entry point called sympy that calls sympy.main()
+
+
+  # If this is a new build for the same version, increment the build
+  # number. If you do not include this key, it defaults to 0.
+  # number: 1
+
+requirements:
+  build:
+    - python
+
+  run:
+    - python
+
+test:
+  # Python imports
+  imports:
+    - sympy
+    - sympy.assumptions
+    - sympy.assumptions.handlers
+    - sympy.assumptions.tests
+    - sympy.calculus
+    - sympy.calculus.tests
+    - sympy.categories
+    - sympy.categories.tests
+    - sympy.combinatorics
+    - sympy.combinatorics.tests
+    - sympy.concrete
+    - sympy.concrete.tests
+    - sympy.core
+    - sympy.core.tests
+    - sympy.crypto
+    - sympy.crypto.tests
+    - sympy.diffgeom
+    - sympy.diffgeom.tests
+    - sympy.external
+    - sympy.external.tests
+    - sympy.functions
+    - sympy.functions.combinatorial
+    - sympy.functions.combinatorial.tests
+    - sympy.functions.elementary
+    - sympy.functions.elementary.tests
+    - sympy.functions.special
+    - sympy.functions.special.tests
+    - sympy.galgebra
+    - sympy.galgebra.tests
+    - sympy.geometry
+    - sympy.geometry.tests
+    - sympy.integrals
+    - sympy.integrals.tests
+    - sympy.interactive
+    - sympy.interactive.tests
+    - sympy.liealgebras
+    - sympy.liealgebras.tests
+    - sympy.logic
+    - sympy.logic.algorithms
+    - sympy.logic.tests
+    - sympy.logic.utilities
+    - sympy.matrices
+    - sympy.matrices.expressions
+    - sympy.matrices.expressions.tests
+    - sympy.matrices.tests
+    - sympy.mpmath
+    - sympy.mpmath.calculus
+    - sympy.mpmath.functions
+    - sympy.mpmath.libmp
+    - sympy.mpmath.matrices
+    - sympy.mpmath.tests
+    - sympy.ntheory
+    - sympy.ntheory.tests
+    - sympy.parsing
+    - sympy.parsing.tests
+    - sympy.physics
+    - sympy.physics.hep
+    - sympy.physics.hep.tests
+    - sympy.physics.mechanics
+    - sympy.physics.mechanics.tests
+    - sympy.physics.quantum
+    - sympy.physics.quantum.tests
+    - sympy.physics.tests
+    - sympy.physics.vector
+    - sympy.physics.vector.tests
+    - sympy.plotting
+    - sympy.plotting.intervalmath
+    - sympy.plotting.intervalmath.tests
+    - sympy.plotting.pygletplot
+    - sympy.plotting.pygletplot.tests
+    - sympy.plotting.tests
+    - sympy.polys
+    - sympy.polys.agca
+    - sympy.polys.agca.tests
+    - sympy.polys.domains
+    - sympy.polys.domains.tests
+    - sympy.polys.tests
+    - sympy.printing
+    - sympy.printing.pretty
+    - sympy.printing.pretty.tests
+    - sympy.printing.tests
+    - sympy.series
+    - sympy.series.tests
+    - sympy.sets
+    - sympy.sets.tests
+    - sympy.simplify
+    - sympy.simplify.tests
+    - sympy.solvers
+    - sympy.solvers.tests
+    - sympy.statistics
+    - sympy.statistics.tests
+    - sympy.stats
+    - sympy.stats.tests
+    - sympy.strategies
+    - sympy.strategies.branch
+    - sympy.strategies.branch.tests
+    - sympy.strategies.tests
+    - sympy.tensor
+    - sympy.tensor.tests
+    - sympy.unify
+    - sympy.unify.tests
+    - sympy.utilities
+    - sympy.utilities.mathml
+    - sympy.utilities.tests
+
+  #commands:
+    # You can put test commands to be run here.  Use this to test that the
+    # entry points work.
+
+
+  # You can also put a file called run_test.py in the recipe that will be run
+  # at test time.
+
+  # requires:
+    # Put any additional test requirements here.  For example
+    # - nose
+
+about:
+  home: http://code.google.com/p/sympy
+  license: BSD License
+  summary: 'Computer algebra system (CAS) in Python'
+
+# See
+# http://docs.continuum.io/conda/build.html for
+# more information about meta.yaml
diff --git a/tests/test-skeleton/sympy-0.7.5/bld.bat b/tests/test-skeleton/sympy-0.7.5/bld.bat
new file mode 100644
--- /dev/null
+++ b/tests/test-skeleton/sympy-0.7.5/bld.bat
@@ -0,0 +1,8 @@
+"%PYTHON%" setup.py install
+if errorlevel 1 exit 1
+
+:: Add more build steps here, if they are necessary.
+
+:: See
+:: http://docs.continuum.io/conda/build.html
+:: for a list of environment variables that are set during the build process.
diff --git a/tests/test-skeleton/sympy-0.7.5/build.sh b/tests/test-skeleton/sympy-0.7.5/build.sh
new file mode 100644
--- /dev/null
+++ b/tests/test-skeleton/sympy-0.7.5/build.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+$PYTHON setup.py install
+
+# Add more build steps here, if they are necessary.
+
+# See
+# http://docs.continuum.io/conda/build.html
+# for a list of environment variables that are set during the build process.
diff --git a/tests/test-skeleton/sympy-0.7.5/meta.yaml b/tests/test-skeleton/sympy-0.7.5/meta.yaml
new file mode 100644
--- /dev/null
+++ b/tests/test-skeleton/sympy-0.7.5/meta.yaml
@@ -0,0 +1,162 @@
+package:
+  name: sympy
+  version: !!str 0.7.5
+
+source:
+  fn: sympy-0.7.5.tar.gz
+  url: https://pypi.python.org/packages/source/s/sympy/sympy-0.7.5.tar.gz
+  md5: 7de1adb49972a15a3dd975e879a2bea9
+#  patches:
+   # List any patch files here
+   # - fix.patch
+
+# build:
+  #preserve_egg_dir: True
+  #entry_points:
+    # Put any entry points (scripts to be generated automatically) here. The
+    # syntax is module:function.  For example
+    #
+    # - sympy = sympy:main
+    #
+    # Would create an entry point called sympy that calls sympy.main()
+
+
+  # If this is a new build for the same version, increment the build
+  # number. If you do not include this key, it defaults to 0.
+  # number: 1
+
+requirements:
+  build:
+    - python
+
+  run:
+    - python
+
+test:
+  # Python imports
+  imports:
+    - sympy
+    - sympy.assumptions
+    - sympy.assumptions.handlers
+    - sympy.assumptions.tests
+    - sympy.calculus
+    - sympy.calculus.tests
+    - sympy.categories
+    - sympy.categories.tests
+    - sympy.combinatorics
+    - sympy.combinatorics.tests
+    - sympy.concrete
+    - sympy.concrete.tests
+    - sympy.core
+    - sympy.core.tests
+    - sympy.crypto
+    - sympy.crypto.tests
+    - sympy.diffgeom
+    - sympy.diffgeom.tests
+    - sympy.external
+    - sympy.external.tests
+    - sympy.functions
+    - sympy.functions.combinatorial
+    - sympy.functions.combinatorial.tests
+    - sympy.functions.elementary
+    - sympy.functions.elementary.tests
+    - sympy.functions.special
+    - sympy.functions.special.tests
+    - sympy.galgebra
+    - sympy.galgebra.tests
+    - sympy.geometry
+    - sympy.geometry.tests
+    - sympy.integrals
+    - sympy.integrals.tests
+    - sympy.interactive
+    - sympy.interactive.tests
+    - sympy.liealgebras
+    - sympy.liealgebras.tests
+    - sympy.logic
+    - sympy.logic.algorithms
+    - sympy.logic.tests
+    - sympy.logic.utilities
+    - sympy.matrices
+    - sympy.matrices.expressions
+    - sympy.matrices.expressions.tests
+    - sympy.matrices.tests
+    - sympy.mpmath
+    - sympy.mpmath.calculus
+    - sympy.mpmath.functions
+    - sympy.mpmath.libmp
+    - sympy.mpmath.matrices
+    - sympy.mpmath.tests
+    - sympy.ntheory
+    - sympy.ntheory.tests
+    - sympy.parsing
+    - sympy.parsing.tests
+    - sympy.physics
+    - sympy.physics.hep
+    - sympy.physics.hep.tests
+    - sympy.physics.mechanics
+    - sympy.physics.mechanics.tests
+    - sympy.physics.quantum
+    - sympy.physics.quantum.tests
+    - sympy.physics.tests
+    - sympy.physics.vector
+    - sympy.physics.vector.tests
+    - sympy.plotting
+    - sympy.plotting.intervalmath
+    - sympy.plotting.intervalmath.tests
+    - sympy.plotting.pygletplot
+    - sympy.plotting.pygletplot.tests
+    - sympy.plotting.tests
+    - sympy.polys
+    - sympy.polys.agca
+    - sympy.polys.agca.tests
+    - sympy.polys.domains
+    - sympy.polys.domains.tests
+    - sympy.polys.tests
+    - sympy.printing
+    - sympy.printing.pretty
+    - sympy.printing.pretty.tests
+    - sympy.printing.tests
+    - sympy.series
+    - sympy.series.tests
+    - sympy.sets
+    - sympy.sets.tests
+    - sympy.simplify
+    - sympy.simplify.tests
+    - sympy.solvers
+    - sympy.solvers.tests
+    - sympy.statistics
+    - sympy.statistics.tests
+    - sympy.stats
+    - sympy.stats.tests
+    - sympy.strategies
+    - sympy.strategies.branch
+    - sympy.strategies.branch.tests
+    - sympy.strategies.tests
+    - sympy.tensor
+    - sympy.tensor.tests
+    - sympy.unify
+    - sympy.unify.tests
+    - sympy.utilities
+    - sympy.utilities.mathml
+    - sympy.utilities.tests
+
+  #commands:
+    # You can put test commands to be run here.  Use this to test that the
+    # entry points work.
+
+
+  # You can also put a file called run_test.py in the recipe that will be run
+  # at test time.
+
+  # requires:
+    # Put any additional test requirements here.  For example
+    # - nose
+
+about:
+  home: http://code.google.com/p/sympy
+  license:  BSD License
+  summary: 'Computer algebra system (CAS) in Python'
+
+# See
+# http://docs.continuum.io/conda/build.html for
+# more information about meta.yaml
diff --git a/tests/test-skeleton/test-skeleton.sh b/tests/test-skeleton/test-skeleton.sh
new file mode 100755
--- /dev/null
+++ b/tests/test-skeleton/test-skeleton.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+set -x
+set -e
+DIR=`mktemp -d -t sympy-0.7.5-XXXX`
+conda skeleton pypi --output-dir $DIR --version=0.7.5 sympy
+python -c "
+import yaml
+with open('"$(dirname "${BASH_SOURCE[0]}")"/sympy-0.7.5/meta.yaml') as f:
+    expected = yaml.load(f)
+with open('$DIR/sympy/meta.yaml') as f:
+    actual = yaml.load(f)
+assert expected == actual
+"
+# XXX: This won't run if the test fails.
+rm -rf $DIR
+echo passed
+
+DIR=`mktemp -d -t sympy-0.7.5-url-XXXX`
+conda skeleton pypi --output-dir $DIR https://pypi.python.org/packages/source/s/sympy/sympy-0.7.5.tar.gz#md5=7de1adb49972a15a3dd975e879a2bea9
+python -c "
+import yaml
+with open('"$(dirname "${BASH_SOURCE[0]}")"/sympy-0.7.5-url/meta.yaml') as f:
+    expected = yaml.load(f)
+with open('$DIR/sympy/meta.yaml') as f:
+    actual = yaml.load(f)
+assert expected == actual
+"
+# XXX: This won't run if the test fails.
+rm -rf $DIR
+echo passed