Beispiel #1
0
def test_addenv_setpython(pkg):
    replog = ResultLog()
    replog.set_header(installpkg=pkg)
    envlog = replog.get_envlog("py26")
    envlog.set_python_info(py.path.local(sys.executable))
    assert envlog.dict["python"]["version_info"] == list(sys.version_info)
    assert envlog.dict["python"]["version"] == sys.version
    assert envlog.dict["python"]["executable"] == sys.executable
Beispiel #2
0
def test_addenv_setpython(pkg):
    replog = ResultLog()
    replog.set_header(installpkg=pkg)
    envlog = replog.get_envlog("py26")
    envlog.set_python_info(py.path.local(sys.executable))
    assert envlog.dict["python"]["version_info"] == list(sys.version_info)
    assert envlog.dict["python"]["version"] == sys.version
    assert envlog.dict["python"]["executable"] == sys.executable
Beispiel #3
0
def test_get_commandlog(pkg):
    replog = ResultLog()
    replog.set_header(installpkg=pkg)
    envlog = replog.get_envlog("py26")
    assert "setup" not in envlog.dict
    setuplog = envlog.get_commandlog("setup")
    setuplog.add_command(["virtualenv", "..."], "venv created", 0)
    assert setuplog.list == [{"command": ["virtualenv", "..."], "output": "venv created", "retcode": "0"}]
    assert envlog.dict["setup"]
    setuplog2 = replog.get_envlog("py26").get_commandlog("setup")
    assert setuplog2.list == setuplog.list
Beispiel #4
0
def test_pre_set_header(pkg):
    replog = ResultLog()
    d = replog.dict
    assert replog.dict == d
    assert replog.dict["reportversion"] == "1"
    assert replog.dict["toxversion"] == tox_plus.__version__
    assert replog.dict["platform"] == sys.platform
    assert replog.dict["host"] == py.std.socket.getfqdn()
    data = replog.dumps_json()
    replog2 = ResultLog.loads_json(data)
    assert replog2.dict == replog.dict
Beispiel #5
0
def test_pre_set_header(pkg):
    replog = ResultLog()
    d = replog.dict
    assert replog.dict == d
    assert replog.dict["reportversion"] == "1"
    assert replog.dict["toxversion"] == tox_plus.__version__
    assert replog.dict["platform"] == sys.platform
    assert replog.dict["host"] == py.std.socket.getfqdn()
    data = replog.dumps_json()
    replog2 = ResultLog.loads_json(data)
    assert replog2.dict == replog.dict
Beispiel #6
0
 def __init__(self, config, popen=subprocess.Popen, Report=Reporter):
     self.config = config
     self.popen = popen
     self.resultlog = ResultLog()
     self.report = Report(self)
     self.make_emptydir(config.logdir)
     config.logdir.ensure(dir=1)
     # self.report.using("logdir %s" %(self.config.logdir,))
     self.report.using("tox.ini: %s" % (self.config.toxinipath, ))
     self._spec2pkg = {}
     self._name2venv = {}
     try:
         self.venvlist = [self.getvenv(x) for x in self.config.envlist]
     except LookupError:
         raise SystemExit(1)
     self._actions = []
Beispiel #7
0
def test_set_header(pkg):
    replog = ResultLog()
    d = replog.dict
    replog.set_header(installpkg=pkg)
    assert replog.dict == d
    assert replog.dict["reportversion"] == "1"
    assert replog.dict["toxversion"] == tox_plus.__version__
    assert replog.dict["platform"] == sys.platform
    assert replog.dict["host"] == py.std.socket.getfqdn()
    assert replog.dict["installpkg"] == {
        "basename": "hello-1.0.tar.gz",
        "md5": pkg.computehash("md5"),
        "sha256": pkg.computehash("sha256"),
    }
    data = replog.dumps_json()
    replog2 = ResultLog.loads_json(data)
    assert replog2.dict == replog.dict
Beispiel #8
0
def test_get_commandlog(pkg):
    replog = ResultLog()
    replog.set_header(installpkg=pkg)
    envlog = replog.get_envlog("py26")
    assert "setup" not in envlog.dict
    setuplog = envlog.get_commandlog("setup")
    setuplog.add_command(["virtualenv", "..."], "venv created", 0)
    assert setuplog.list == [{"command": ["virtualenv", "..."],
                              "output": "venv created",
                              "retcode": "0"}]
    assert envlog.dict["setup"]
    setuplog2 = replog.get_envlog("py26").get_commandlog("setup")
    assert setuplog2.list == setuplog.list
Beispiel #9
0
 def __init__(self, config, popen=subprocess.Popen, Report=Reporter):
     self.config = config
     self.popen = popen
     self.resultlog = ResultLog()
     self.report = Report(self)
     self.make_emptydir(config.logdir)
     config.logdir.ensure(dir=1)
     # self.report.using("logdir %s" %(self.config.logdir,))
     self.report.using("tox.ini: %s" % (self.config.toxinipath,))
     self._spec2pkg = {}
     self._name2venv = {}
     try:
         self.venvlist = [
             self.getvenv(x)
             for x in self.config.envlist
         ]
     except LookupError:
         raise SystemExit(1)
     self._actions = []
Beispiel #10
0
def test_set_header(pkg):
    replog = ResultLog()
    d = replog.dict
    replog.set_header(installpkg=pkg)
    assert replog.dict == d
    assert replog.dict["reportversion"] == "1"
    assert replog.dict["toxversion"] == tox_plus.__version__
    assert replog.dict["platform"] == sys.platform
    assert replog.dict["host"] == py.std.socket.getfqdn()
    assert replog.dict["installpkg"] == {
        "basename": "hello-1.0.tar.gz",
        "md5": pkg.computehash("md5"),
        "sha256": pkg.computehash("sha256")}
    data = replog.dumps_json()
    replog2 = ResultLog.loads_json(data)
    assert replog2.dict == replog.dict
Beispiel #11
0
class Session:
    def __init__(self, config, popen=subprocess.Popen, Report=Reporter):
        self.config = config
        self.popen = popen
        self.resultlog = ResultLog()
        self.report = Report(self)
        self.make_emptydir(config.logdir)
        config.logdir.ensure(dir=1)
        # self.report.using("logdir %s" %(self.config.logdir,))
        self.report.using("tox.ini: %s" % (self.config.toxinipath, ))
        self._spec2pkg = {}
        self._name2venv = {}
        try:
            self.venvlist = [self.getvenv(x) for x in self.config.envlist]
        except LookupError:
            raise SystemExit(1)
        self._actions = []

    def _makevenv(self, name):
        envconfig = self.config.envconfigs.get(name, None)
        if envconfig is None:
            self.report.error("unknown environment %r" % name)
            raise LookupError(name)
        venv = VirtualEnv(envconfig=envconfig, session=self)
        self._name2venv[name] = venv
        return venv

    def getvenv(self, name):
        """ return a VirtualEnv controler object for the 'name' env.  """
        try:
            return self._name2venv[name]
        except KeyError:
            return self._makevenv(name)

    def newaction(self, venv, msg, *args):
        action = Action(self, venv, msg, args)
        self._actions.append(action)
        return action

    def runcommand(self):
        self.report.using("tox-%s from %s" %
                          (tox_plus.__version__, tox_plus.__file__))
        if self.config.minversion:
            minversion = NormalizedVersion(self.config.minversion)
            toxversion = NormalizedVersion(tox_plus.__version__)
            if toxversion < minversion:
                self.report.error(
                    "tox version is %s, required is at least %s" %
                    (toxversion, minversion))
                raise SystemExit(1)
        if self.config.option.showconfig:
            self.showconfig()
        elif self.config.option.listenvs:
            self.showenvs()
        else:
            return self.subcommand_test()

    def _copyfiles(self, srcdir, pathlist, destdir):
        for relpath in pathlist:
            src = srcdir.join(relpath)
            if not src.check():
                self.report.error("missing source file: %s" % (src, ))
                raise SystemExit(1)
            target = destdir.join(relpath)
            target.dirpath().ensure(dir=1)
            src.copy(target)

    def _makesdist(self):
        setup = self.config.setupdir.join("setup.py")
        if not setup.check():
            raise tox_plus.exception.MissingFile(setup)
        action = self.newaction(None, "packaging")
        with action:
            action.setactivity("sdist-make", setup)
            self.make_emptydir(self.config.distdir)
            action.popen([
                sys.executable,
                setup,
                "sdist",
                "--formats=zip",
                "--dist-dir",
                self.config.distdir,
            ],
                         cwd=self.config.setupdir)
            try:
                return self.config.distdir.listdir()[0]
            except py.error.ENOENT:
                # check if empty or comment only
                data = []
                with open(str(setup)) as fp:
                    for line in fp:
                        if line and line[0] == '#':
                            continue
                        data.append(line)
                if not ''.join(data).strip():
                    self.report.error('setup.py is empty')
                    raise SystemExit(1)
                self.report.error(
                    'No dist directory found. Please check setup.py, e.g with:\n'
                    '     python setup.py sdist')
                raise SystemExit(1)

    def make_emptydir(self, path):
        if path.check():
            self.report.info("  removing %s" % path)
            py.std.shutil.rmtree(str(path), ignore_errors=True)
            path.ensure(dir=1)

    def setupenv(self, venv):
        if not venv.matching_platform():
            venv.status = "platform mismatch"
            return  # we simply omit non-matching platforms
        action = self.newaction(venv, "getenv", venv.envconfig.envdir)
        with action:
            venv.status = 0
            envlog = self.resultlog.get_envlog(venv.name)
            try:
                status = venv.update(action=action)
            except tox_plus.exception.InvocationError:
                status = sys.exc_info()[1]
            if status:
                commandlog = envlog.get_commandlog("setup")
                commandlog.add_command(["setup virtualenv"], str(status), 1)
                venv.status = status
                self.report.error(str(status))
                return False
            commandpath = venv.getcommandpath("python")
            envlog.set_python_info(commandpath)
            return True

    def finishvenv(self, venv):
        action = self.newaction(venv, "finishvenv")
        with action:
            venv.finish()
            return True

    def developpkg(self, venv, setupdir):
        action = self.newaction(venv, "developpkg", setupdir)
        with action:
            try:
                venv.developpkg(setupdir, action)
                return True
            except tox_plus.exception.InvocationError:
                venv.status = sys.exc_info()[1]
                return False

    def installpkg(self, venv, path):
        """Install package in the specified virtual environment.

        :param :class:`tox.config.VenvConfig`: Destination environment
        :param str path: Path to the distribution package.
        :return: True if package installed otherwise False.
        :rtype: bool
        """
        self.resultlog.set_header(installpkg=py.path.local(path))
        action = self.newaction(venv, "installpkg", path)
        with action:
            try:
                venv.installpkg(path, action)
                return True
            except tox_plus.exception.InvocationError:
                venv.status = sys.exc_info()[1]
                return False

    def get_installpkg_path(self):
        """
        :return: Path to the distribution
        :rtype: py.path.local
        """
        if not self.config.option.sdistonly and (
                self.config.sdistsrc or self.config.option.installpkg):
            path = self.config.option.installpkg
            if not path:
                path = self.config.sdistsrc
            path = self._resolve_pkg(path)
            self.report.info("using package %r, skipping 'sdist' activity " %
                             str(path))
        else:
            try:
                path = self._makesdist()
            except tox_plus.exception.InvocationError:
                v = sys.exc_info()[1]
                self.report.error("FAIL could not package project - v = %r" %
                                  v)
                return
            sdistfile = self.config.distshare.join(path.basename)
            if sdistfile != path:
                self.report.info("copying new sdistfile to %r" %
                                 str(sdistfile))
                try:
                    sdistfile.dirpath().ensure(dir=1)
                except py.error.Error:
                    self.report.warning("could not copy distfile to %s" %
                                        sdistfile.dirpath())
                else:
                    path.copy(sdistfile)
        return path

    def subcommand_test(self):
        if self.config.skipsdist:
            self.report.info("skipping sdist step")
            path = None
        else:
            path = self.get_installpkg_path()
            if not path:
                return 2
        if self.config.option.sdistonly:
            return
        for venv in self.venvlist:
            if self.setupenv(venv):
                if venv.envconfig.usedevelop:
                    self.developpkg(venv, self.config.setupdir)
                elif self.config.skipsdist or venv.envconfig.skip_install:
                    self.finishvenv(venv)
                else:
                    self.installpkg(venv, path)

                # write out version dependency information
                action = self.newaction(venv, "envreport")
                with action:
                    pip = venv.getcommandpath("pip")
                    # we can't really call internal helpers here easily :/
                    # output = venv._pcall([str(pip), "freeze"],
                    #                      cwd=self.config.toxinidir,
                    #                      action=action)
                    output = py.process.cmdexec("%s freeze" % (pip))
                    packages = output.strip().split("\n")
                    action.setactivity("installed", ",".join(packages))
                    envlog = self.resultlog.get_envlog(venv.name)
                    envlog.set_installed(packages)
                pm = get_plugin_manager()
                pm.hook.pretest(venv=venv)
                self.runtestenv(venv)
                pm.hook.posttest(venv=venv)
        retcode = self._summary()
        return retcode

    def runtestenv(self, venv, redirect=False):
        if not self.config.option.notest:
            if venv.status:
                return
            venv.test(redirect=redirect)
        else:
            venv.status = "skipped tests"

    def _summary(self):
        self.report.startsummary()
        retcode = 0
        for venv in self.venvlist:
            status = venv.status
            if isinstance(status, tox_plus.exception.InterpreterNotFound):
                msg = "  %s: %s" % (venv.envconfig.envname, str(status))
                if self.config.option.skip_missing_interpreters:
                    self.report.skip(msg)
                else:
                    retcode = 1
                    self.report.error(msg)
            elif status == "platform mismatch":
                msg = "  %s: %s" % (venv.envconfig.envname, str(status))
                self.report.skip(msg)
            elif status and status != "skipped tests":
                msg = "  %s: %s" % (venv.envconfig.envname, str(status))
                self.report.error(msg)
                retcode = 1
            else:
                if not status:
                    status = "commands succeeded"
                self.report.good("  %s: %s" % (venv.envconfig.envname, status))
        if not retcode:
            self.report.good("  congratulations :)")

        path = self.config.option.resultjson
        if path:
            path = py.path.local(path)
            path.write(self.resultlog.dumps_json())
            self.report.line("wrote json report at: %s" % path)
        return retcode

    def showconfig(self):
        self.info_versions()
        self.report.keyvalue("config-file:", self.config.option.configfile)
        self.report.keyvalue("toxinipath: ", self.config.toxinipath)
        self.report.keyvalue("toxinidir:  ", self.config.toxinidir)
        self.report.keyvalue("toxworkdir: ", self.config.toxworkdir)
        self.report.keyvalue("setupdir:   ", self.config.setupdir)
        self.report.keyvalue("distshare:  ", self.config.distshare)
        self.report.keyvalue("skipsdist:  ", self.config.skipsdist)
        self.report.tw.line()
        for envconfig in self.config.envconfigs.values():
            self.report.line("[testenv:%s]" % envconfig.envname, bold=True)
            for attr in self.config._parser._testenv_attr:
                self.report.line("  %-15s = %s" %
                                 (attr.name, getattr(envconfig, attr.name)))

    def showenvs(self):
        for env in self.config.envlist:
            self.report.line("%s" % env)

    def info_versions(self):
        versions = ['tox-%s' % tox_plus.__version__]
        try:
            version = py.process.cmdexec("virtualenv --version")
        except py.process.cmdexec.Error:
            versions.append("virtualenv-1.9.1 (vendored)")
        else:
            versions.append("virtualenv-%s" % version.strip())
        self.report.keyvalue("tool-versions:", " ".join(versions))

    def _resolve_pkg(self, pkgspec):
        try:
            return self._spec2pkg[pkgspec]
        except KeyError:
            self._spec2pkg[pkgspec] = x = self._resolvepkg(pkgspec)
            return x

    def _resolvepkg(self, pkgspec):
        if not os.path.isabs(str(pkgspec)):
            return pkgspec
        p = py.path.local(pkgspec)
        if p.check():
            return p
        if not p.dirpath().check(dir=1):
            raise tox_plus.exception.MissingDirectory(p.dirpath())
        self.report.info("determining %s" % p)
        candidates = p.dirpath().listdir(p.basename)
        if len(candidates) == 0:
            raise tox_plus.exception.MissingDependency(pkgspec)
        if len(candidates) > 1:
            items = []
            for x in candidates:
                ver = getversion(x.basename)
                if ver is not None:
                    items.append((ver, x))
                else:
                    self.report.warning("could not determine version of: %s" %
                                        str(x))
            items.sort()
            if not items:
                raise tox_plus.exception.MissingDependency(pkgspec)
            return items[-1][1]
        else:
            return candidates[0]
Beispiel #12
0
class Session:

    def __init__(self, config, popen=subprocess.Popen, Report=Reporter):
        self.config = config
        self.popen = popen
        self.resultlog = ResultLog()
        self.report = Report(self)
        self.make_emptydir(config.logdir)
        config.logdir.ensure(dir=1)
        # self.report.using("logdir %s" %(self.config.logdir,))
        self.report.using("tox.ini: %s" % (self.config.toxinipath,))
        self._spec2pkg = {}
        self._name2venv = {}
        try:
            self.venvlist = [
                self.getvenv(x)
                for x in self.config.envlist
            ]
        except LookupError:
            raise SystemExit(1)
        self._actions = []

    def _makevenv(self, name):
        envconfig = self.config.envconfigs.get(name, None)
        if envconfig is None:
            self.report.error("unknown environment %r" % name)
            raise LookupError(name)
        venv = VirtualEnv(envconfig=envconfig, session=self)
        self._name2venv[name] = venv
        return venv

    def getvenv(self, name):
        """ return a VirtualEnv controler object for the 'name' env.  """
        try:
            return self._name2venv[name]
        except KeyError:
            return self._makevenv(name)

    def newaction(self, venv, msg, *args):
        action = Action(self, venv, msg, args)
        self._actions.append(action)
        return action

    def runcommand(self):
        self.report.using("tox-%s from %s" % (tox_plus.__version__, tox_plus.__file__))
        if self.config.minversion:
            minversion = NormalizedVersion(self.config.minversion)
            toxversion = NormalizedVersion(tox_plus.__version__)
            if toxversion < minversion:
                self.report.error(
                    "tox version is %s, required is at least %s" % (
                        toxversion, minversion))
                raise SystemExit(1)
        if self.config.option.showconfig:
            self.showconfig()
        elif self.config.option.listenvs:
            self.showenvs()
        else:
            return self.subcommand_test()

    def _copyfiles(self, srcdir, pathlist, destdir):
        for relpath in pathlist:
            src = srcdir.join(relpath)
            if not src.check():
                self.report.error("missing source file: %s" % (src,))
                raise SystemExit(1)
            target = destdir.join(relpath)
            target.dirpath().ensure(dir=1)
            src.copy(target)

    def _makesdist(self):
        setup = self.config.setupdir.join("setup.py")
        if not setup.check():
            raise tox_plus.exception.MissingFile(setup)
        action = self.newaction(None, "packaging")
        with action:
            action.setactivity("sdist-make", setup)
            self.make_emptydir(self.config.distdir)
            action.popen([sys.executable, setup, "sdist", "--formats=zip",
                          "--dist-dir", self.config.distdir, ],
                         cwd=self.config.setupdir)
            try:
                return self.config.distdir.listdir()[0]
            except py.error.ENOENT:
                # check if empty or comment only
                data = []
                with open(str(setup)) as fp:
                    for line in fp:
                        if line and line[0] == '#':
                            continue
                        data.append(line)
                if not ''.join(data).strip():
                    self.report.error(
                        'setup.py is empty'
                    )
                    raise SystemExit(1)
                self.report.error(
                    'No dist directory found. Please check setup.py, e.g with:\n'
                    '     python setup.py sdist'
                )
                raise SystemExit(1)

    def make_emptydir(self, path):
        if path.check():
            self.report.info("  removing %s" % path)
            py.std.shutil.rmtree(str(path), ignore_errors=True)
            path.ensure(dir=1)

    def setupenv(self, venv):
        if not venv.matching_platform():
            venv.status = "platform mismatch"
            return  # we simply omit non-matching platforms
        action = self.newaction(venv, "getenv", venv.envconfig.envdir)
        with action:
            venv.status = 0
            envlog = self.resultlog.get_envlog(venv.name)
            try:
                status = venv.update(action=action)
            except tox_plus.exception.InvocationError:
                status = sys.exc_info()[1]
            if status:
                commandlog = envlog.get_commandlog("setup")
                commandlog.add_command(["setup virtualenv"], str(status), 1)
                venv.status = status
                self.report.error(str(status))
                return False
            commandpath = venv.getcommandpath("python")
            envlog.set_python_info(commandpath)
            return True

    def finishvenv(self, venv):
        action = self.newaction(venv, "finishvenv")
        with action:
            venv.finish()
            return True

    def developpkg(self, venv, setupdir):
        action = self.newaction(venv, "developpkg", setupdir)
        with action:
            try:
                venv.developpkg(setupdir, action)
                return True
            except tox_plus.exception.InvocationError:
                venv.status = sys.exc_info()[1]
                return False

    def installpkg(self, venv, path):
        """Install package in the specified virtual environment.

        :param :class:`tox.config.VenvConfig`: Destination environment
        :param str path: Path to the distribution package.
        :return: True if package installed otherwise False.
        :rtype: bool
        """
        self.resultlog.set_header(installpkg=py.path.local(path))
        action = self.newaction(venv, "installpkg", path)
        with action:
            try:
                venv.installpkg(path, action)
                return True
            except tox_plus.exception.InvocationError:
                venv.status = sys.exc_info()[1]
                return False

    def get_installpkg_path(self):
        """
        :return: Path to the distribution
        :rtype: py.path.local
        """
        if not self.config.option.sdistonly and (self.config.sdistsrc or
                                                 self.config.option.installpkg):
            path = self.config.option.installpkg
            if not path:
                path = self.config.sdistsrc
            path = self._resolve_pkg(path)
            self.report.info("using package %r, skipping 'sdist' activity " %
                             str(path))
        else:
            try:
                path = self._makesdist()
            except tox_plus.exception.InvocationError:
                v = sys.exc_info()[1]
                self.report.error("FAIL could not package project - v = %r" %
                                  v)
                return
            sdistfile = self.config.distshare.join(path.basename)
            if sdistfile != path:
                self.report.info("copying new sdistfile to %r" %
                                 str(sdistfile))
                try:
                    sdistfile.dirpath().ensure(dir=1)
                except py.error.Error:
                    self.report.warning("could not copy distfile to %s" %
                                        sdistfile.dirpath())
                else:
                    path.copy(sdistfile)
        return path

    def subcommand_test(self):
        if self.config.skipsdist:
            self.report.info("skipping sdist step")
            path = None
        else:
            path = self.get_installpkg_path()
            if not path:
                return 2
        if self.config.option.sdistonly:
            return
        for venv in self.venvlist:
            if self.setupenv(venv):
                if venv.envconfig.usedevelop:
                    self.developpkg(venv, self.config.setupdir)
                elif self.config.skipsdist or venv.envconfig.skip_install:
                    self.finishvenv(venv)
                else:
                    self.installpkg(venv, path)

                # write out version dependency information
                action = self.newaction(venv, "envreport")
                with action:
                    pip = venv.getcommandpath("pip")
                    # we can't really call internal helpers here easily :/
                    # output = venv._pcall([str(pip), "freeze"],
                    #                      cwd=self.config.toxinidir,
                    #                      action=action)
                    output = py.process.cmdexec("%s freeze" % (pip))
                    packages = output.strip().split("\n")
                    action.setactivity("installed", ",".join(packages))
                    envlog = self.resultlog.get_envlog(venv.name)
                    envlog.set_installed(packages)
                pm = get_plugin_manager()
                pm.hook.pretest(venv=venv)
                self.runtestenv(venv)
                pm.hook.posttest(venv=venv)
        retcode = self._summary()
        return retcode

    def runtestenv(self, venv, redirect=False):
        if not self.config.option.notest:
            if venv.status:
                return
            venv.test(redirect=redirect)
        else:
            venv.status = "skipped tests"

    def _summary(self):
        self.report.startsummary()
        retcode = 0
        for venv in self.venvlist:
            status = venv.status
            if isinstance(status, tox_plus.exception.InterpreterNotFound):
                msg = "  %s: %s" % (venv.envconfig.envname, str(status))
                if self.config.option.skip_missing_interpreters:
                    self.report.skip(msg)
                else:
                    retcode = 1
                    self.report.error(msg)
            elif status == "platform mismatch":
                msg = "  %s: %s" % (venv.envconfig.envname, str(status))
                self.report.skip(msg)
            elif status and status != "skipped tests":
                msg = "  %s: %s" % (venv.envconfig.envname, str(status))
                self.report.error(msg)
                retcode = 1
            else:
                if not status:
                    status = "commands succeeded"
                self.report.good("  %s: %s" % (venv.envconfig.envname, status))
        if not retcode:
            self.report.good("  congratulations :)")

        path = self.config.option.resultjson
        if path:
            path = py.path.local(path)
            path.write(self.resultlog.dumps_json())
            self.report.line("wrote json report at: %s" % path)
        return retcode

    def showconfig(self):
        self.info_versions()
        self.report.keyvalue("config-file:", self.config.option.configfile)
        self.report.keyvalue("toxinipath: ", self.config.toxinipath)
        self.report.keyvalue("toxinidir:  ", self.config.toxinidir)
        self.report.keyvalue("toxworkdir: ", self.config.toxworkdir)
        self.report.keyvalue("setupdir:   ", self.config.setupdir)
        self.report.keyvalue("distshare:  ", self.config.distshare)
        self.report.keyvalue("skipsdist:  ", self.config.skipsdist)
        self.report.tw.line()
        for envconfig in self.config.envconfigs.values():
            self.report.line("[testenv:%s]" % envconfig.envname, bold=True)
            for attr in self.config._parser._testenv_attr:
                self.report.line("  %-15s = %s"
                                 % (attr.name, getattr(envconfig, attr.name)))

    def showenvs(self):
        for env in self.config.envlist:
            self.report.line("%s" % env)

    def info_versions(self):
        versions = ['tox-%s' % tox_plus.__version__]
        try:
            version = py.process.cmdexec("virtualenv --version")
        except py.process.cmdexec.Error:
            versions.append("virtualenv-1.9.1 (vendored)")
        else:
            versions.append("virtualenv-%s" % version.strip())
        self.report.keyvalue("tool-versions:", " ".join(versions))

    def _resolve_pkg(self, pkgspec):
        try:
            return self._spec2pkg[pkgspec]
        except KeyError:
            self._spec2pkg[pkgspec] = x = self._resolvepkg(pkgspec)
            return x

    def _resolvepkg(self, pkgspec):
        if not os.path.isabs(str(pkgspec)):
            return pkgspec
        p = py.path.local(pkgspec)
        if p.check():
            return p
        if not p.dirpath().check(dir=1):
            raise tox_plus.exception.MissingDirectory(p.dirpath())
        self.report.info("determining %s" % p)
        candidates = p.dirpath().listdir(p.basename)
        if len(candidates) == 0:
            raise tox_plus.exception.MissingDependency(pkgspec)
        if len(candidates) > 1:
            items = []
            for x in candidates:
                ver = getversion(x.basename)
                if ver is not None:
                    items.append((ver, x))
                else:
                    self.report.warning("could not determine version of: %s" %
                                        str(x))
            items.sort()
            if not items:
                raise tox_plus.exception.MissingDependency(pkgspec)
            return items[-1][1]
        else:
            return candidates[0]