Blame runtests.py

Antoni Segura Puimedon 79d5c3
#!/usr/bin/env python3
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon d70640
from __future__ import print_function, unicode_literals
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
import argparse
Pierre-Yves Chibon 1e58be
import coverage
Pierre-Yves Chibon 1e58be
import json
Pierre-Yves Chibon 1e58be
import logging
Pierre-Yves Chibon 1e58be
import multiprocessing
Pierre-Yves Chibon 1e58be
import os
Pierre-Yves Chibon 1e58be
import shutil
Pierre-Yves Chibon 1e58be
import subprocess
Pierre-Yves Chibon 1e58be
import sys
Pierre-Yves Chibon 1e58be
import threading
Pierre-Yves Chibon 1e58be
import time
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 84e668
RUNNER_PY = "nosetests"
Pierre-Yves Chibon 1e58be
RUNNER_PY2 = "nosetests-2"
Pierre-Yves Chibon 1e58be
RUNNER_PY3 = "nosetests-3"
Pierre-Yves Chibon 84e668
COVER_PY = "coverage"
Pierre-Yves Chibon 1e58be
COVER_PY2 = "coverage2"
Pierre-Yves Chibon 1e58be
COVER_PY3 = "coverage3"
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
LASTLEN = None
Pierre-Yves Chibon 1e58be
NUMREMAINING = None
Pierre-Yves Chibon 1e58be
PRINTLOCK = None
Pierre-Yves Chibon 1e58be
RUNNING = []
Pierre-Yves Chibon 1e58be
FAILED = []
Pierre-Yves Chibon 1e58be
NUMPROCS = multiprocessing.cpu_count() - 1
Pierre-Yves Chibon bc2063
if os.environ.get('BUILD_ID'):
Pierre-Yves Chibon bc2063
    NUMPROCS = multiprocessing.cpu_count()
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
def setup_parser():
Pierre-Yves Chibon 1e58be
    """ Set up the command line arguments supported and return the arguments
Pierre-Yves Chibon 1e58be
    """
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    parser = argparse.ArgumentParser(description="Run the Pagure tests")
Pierre-Yves Chibon 1e58be
    parser.add_argument(
Pierre-Yves Chibon 1e58be
        "--debug",
Pierre-Yves Chibon 1e58be
        dest="debug",
Pierre-Yves Chibon 1e58be
        action="store_true",
Pierre-Yves Chibon 1e58be
        default=False,
Pierre-Yves Chibon 1e58be
        help="Increase the level of data logged.",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    subparsers = parser.add_subparsers(title="actions")
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    # RUN
Pierre-Yves Chibon 1e58be
    parser_run = subparsers.add_parser("run", help="Run the tests")
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--py2",
Pierre-Yves Chibon 1e58be
        dest="py2",
Pierre-Yves Chibon 1e58be
        action="store_true",
Pierre-Yves Chibon 1e58be
        default=False,
Pierre-Yves Chibon 1e58be
        help="Runs the tests only in python2 instead of both python2 and python3",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--py3",
Pierre-Yves Chibon 1e58be
        dest="py3",
Pierre-Yves Chibon 1e58be
        action="store_true",
Pierre-Yves Chibon 1e58be
        default=False,
Pierre-Yves Chibon 1e58be
        help="Runs the tests only in python3 instead of both python2 and python3",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--results",
Pierre-Yves Chibon 1e58be
        default="results",
Pierre-Yves Chibon 1e58be
        help="Specify a folder in which the results should be placed "
Pierre-Yves Chibon 1e58be
        "(defaults to `results`)",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "-f",
Pierre-Yves Chibon 1e58be
        "--force",
Pierre-Yves Chibon 1e58be
        default=False,
Pierre-Yves Chibon 1e58be
        action="store_true",
Pierre-Yves Chibon 1e58be
        help="Override the results and newfailed file without asking you",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--with-coverage",
Pierre-Yves Chibon 1e58be
        default=False,
Pierre-Yves Chibon 1e58be
        action="store_true",
Pierre-Yves Chibon 1e58be
        help="Also build coverage report",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "failed_tests",
Pierre-Yves Chibon 1e58be
        nargs="?",
Pierre-Yves Chibon 1e58be
        help="File containing a JSON list of the failed tests to run or "
Pierre-Yves Chibon 1e58be
        "pointing to a test file to run.",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.set_defaults(func=do_run)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    # RERUN
Pierre-Yves Chibon 1e58be
    parser_run = subparsers.add_parser("rerun", help="Run failed tests")
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--debug",
Pierre-Yves Chibon 1e58be
        dest="debug",
Pierre-Yves Chibon 1e58be
        action="store_true",
Pierre-Yves Chibon 1e58be
        default=False,
Pierre-Yves Chibon 1e58be
        help="Expand the level of data returned.",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--py2",
Pierre-Yves Chibon 1e58be
        dest="py2",
Pierre-Yves Chibon 1e58be
        action="store_true",
Pierre-Yves Chibon 1e58be
        default=False,
Pierre-Yves Chibon 1e58be
        help="Runs the tests only in python2 instead of both python2 and python3",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--py3",
Pierre-Yves Chibon 1e58be
        dest="py3",
Pierre-Yves Chibon 1e58be
        action="store_true",
Pierre-Yves Chibon 1e58be
        default=False,
Pierre-Yves Chibon 1e58be
        help="Runs the tests only in python3 instead of both python2 and python3",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--results",
Pierre-Yves Chibon 1e58be
        default="results",
Pierre-Yves Chibon 1e58be
        help="Specify a folder in which the results should be placed "
Pierre-Yves Chibon 1e58be
        "(defaults to `results`)",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--with-coverage",
Pierre-Yves Chibon 1e58be
        default=False,
Pierre-Yves Chibon 1e58be
        action="store_true",
Pierre-Yves Chibon 1e58be
        help="Also build coverage report",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.set_defaults(func=do_rerun)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    # LIST
Pierre-Yves Chibon 1e58be
    parser_run = subparsers.add_parser("list", help="List failed tests")
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--results",
Pierre-Yves Chibon 1e58be
        default="results",
Pierre-Yves Chibon 1e58be
        help="Specify a folder in which the results should be placed "
Pierre-Yves Chibon 1e58be
        "(defaults to `results`)",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--show",
Pierre-Yves Chibon 1e58be
        default=False,
Pierre-Yves Chibon 1e58be
        action="store_true",
Pierre-Yves Chibon 1e58be
        help="Show the error files using `less`",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "-n", default=None, nargs="?", type=int,
Pierre-Yves Chibon 1e58be
        help="Number of failed test to show",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.set_defaults(func=do_list)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    # SHOW-COVERAGE
Pierre-Yves Chibon 1e58be
    parser_run = subparsers.add_parser(
Pierre-Yves Chibon 1e58be
        "show-coverage",
Pierre-Yves Chibon 1e58be
        help="Shows the coverage report from the data in the results folder")
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--debug",
Pierre-Yves Chibon 1e58be
        dest="debug",
Pierre-Yves Chibon 1e58be
        action="store_true",
Pierre-Yves Chibon 1e58be
        default=False,
Pierre-Yves Chibon 1e58be
        help="Expand the level of data returned.",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--py2",
Pierre-Yves Chibon 1e58be
        dest="py2",
Pierre-Yves Chibon 1e58be
        action="store_true",
Pierre-Yves Chibon 1e58be
        default=False,
Pierre-Yves Chibon 1e58be
        help="Runs the tests only in python2 instead of both python2 and python3",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--py3",
Pierre-Yves Chibon 1e58be
        dest="py3",
Pierre-Yves Chibon 1e58be
        action="store_true",
Pierre-Yves Chibon 1e58be
        default=False,
Pierre-Yves Chibon 1e58be
        help="Runs the tests only in python3 instead of both python2 and python3",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.add_argument(
Pierre-Yves Chibon 1e58be
        "--results",
Pierre-Yves Chibon 1e58be
        default="results",
Pierre-Yves Chibon 1e58be
        help="Specify a folder in which the results should be placed "
Pierre-Yves Chibon 1e58be
        "(defaults to `results`)",
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
    parser_run.set_defaults(func=do_show_coverage)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    return parser
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
def clean_line():
Pierre-Yves Chibon 1e58be
    global LASTLEN
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    with PRINTLOCK:
Pierre-Yves Chibon 1e58be
        if LASTLEN is not None:
Pierre-Yves Chibon 1e58be
            print(" " * LASTLEN, end="\r")
Pierre-Yves Chibon 1e58be
        LASTLEN = None
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
def print_running():
Pierre-Yves Chibon 1e58be
    global LASTLEN
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    with PRINTLOCK:
Pierre-Yves Chibon 1e58be
        msg = "Running %d suites: %d remaining, %d failed" % (
Pierre-Yves Chibon 1e58be
            len(RUNNING),
Pierre-Yves Chibon 1e58be
            NUMREMAINING,
Pierre-Yves Chibon 1e58be
            len(FAILED),
Pierre-Yves Chibon 1e58be
        )
Pierre-Yves Chibon 1e58be
        LASTLEN = len(msg)
Pierre-Yves Chibon 1e58be
        print(msg, end="\r")
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
def add_running(suite):
Pierre-Yves Chibon 1e58be
    global NUMREMAINING
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    with PRINTLOCK:
Pierre-Yves Chibon 1e58be
        NUMREMAINING -= 1
Pierre-Yves Chibon 1e58be
        RUNNING.append(suite)
Pierre-Yves Chibon 1e58be
        clean_line()
Pierre-Yves Chibon 1e58be
        print_running()
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
def remove_running(suite, failed):
Pierre-Yves Chibon 1e58be
    with PRINTLOCK:
Pierre-Yves Chibon 1e58be
        RUNNING.remove(suite)
Pierre-Yves Chibon 1e58be
        clean_line()
Pierre-Yves Chibon 1e58be
        status = 'passed'
Pierre-Yves Chibon 1e58be
        if failed:
Pierre-Yves Chibon 1e58be
            status = 'FAILED'
Pierre-Yves Chibon 1e58be
        print("Test suite %s: %s" % (status, suite))
Pierre-Yves Chibon 1e58be
        print_running()
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
class WorkerThread(threading.Thread):
Pierre-Yves Chibon 1e58be
    def __init__(self, sem, pyver, suite, results, with_cover):
Pierre-Yves Chibon 05cd04
        name = "py%s-%s" % (pyver, suite)
Pierre-Yves Chibon 1e58be
        super(WorkerThread, self).__init__(name="worker-%s" % name)
Pierre-Yves Chibon 1e58be
        self.name = name
Pierre-Yves Chibon 1e58be
        self.sem = sem
Pierre-Yves Chibon 1e58be
        self.pyver = pyver
Pierre-Yves Chibon 1e58be
        self.suite = suite
Pierre-Yves Chibon 1e58be
        self.failed = None
Pierre-Yves Chibon 1e58be
        self.results = results
Pierre-Yves Chibon 1e58be
        self.with_cover = with_cover
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    def run(self):
Pierre-Yves Chibon 1e58be
        with self.sem:
Pierre-Yves Chibon 1e58be
            add_running(self.name)
Pierre-Yves Chibon 1e58be
            with open(os.path.join(self.results, self.name), "w") as resfile:
Pierre-Yves Chibon 1e58be
                if self.pyver == 2:
Pierre-Yves Chibon 1e58be
                    runner = RUNNER_PY2
Pierre-Yves Chibon 1e58be
                elif self.pyver == 3:
Pierre-Yves Chibon 1e58be
                    runner = RUNNER_PY3
Pierre-Yves Chibon 84e668
                else:
Pierre-Yves Chibon 84e668
                    runner = RUNNER_PY
Pierre-Yves Chibon 1e58be
                cmd = [runner, "-v", "tests.%s" % self.suite]
Pierre-Yves Chibon 1e58be
                if self.with_cover:
Pierre-Yves Chibon 1e58be
                    cmd.append("--with-cover")
Pierre-Yves Chibon ac5025
Pierre-Yves Chibon ac5025
                env = os.environ.copy()
Pierre-Yves Chibon ac5025
                env.update({
Pierre-Yves Chibon 1e58be
                    "PAGURE_CONFIG": "../tests/test_config",
Pierre-Yves Chibon 1e58be
                    "COVERAGE_FILE": os.path.join(
Pierre-Yves Chibon 1e58be
                        self.results, "%s.coverage" % self.name
Pierre-Yves Chibon 1e58be
                    ),
Pierre-Yves Chibon 1e58be
                    "LANG": "en_US.UTF-8",
Pierre-Yves Chibon ac5025
                })
Pierre-Yves Chibon 1e58be
                proc = subprocess.Popen(
Pierre-Yves Chibon b75122
                    cmd, cwd=".", stdout=resfile, stderr=subprocess.STDOUT, env=env
Pierre-Yves Chibon 1e58be
                )
Pierre-Yves Chibon 1e58be
                res = proc.wait()
Pierre-Yves Chibon 1e58be
                if res == 0:
Pierre-Yves Chibon 1e58be
                    self.failed = False
Pierre-Yves Chibon 1e58be
                else:
Pierre-Yves Chibon 1e58be
                    self.failed = True
Pierre-Yves Chibon 1e58be
            if not self.failed is not True:
Pierre-Yves Chibon 1e58be
                with PRINTLOCK:
Pierre-Yves Chibon 1e58be
                    FAILED.append(self.name)
Pierre-Yves Chibon 1e58be
            remove_running(self.name, self.failed)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
def do_run(args):
Pierre-Yves Chibon 1e58be
    """ Performs some checks and runs the tests.
Pierre-Yves Chibon 1e58be
    """
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    # Some pre-flight checks
Pierre-Yves Chibon b75122
    if not os.path.exists("./.git") or not os.path.exists("./nosetests3"):
Pierre-Yves Chibon 1e58be
        print("Please run from a single level into the Pagure codebase")
Pierre-Yves Chibon 1e58be
        return 1
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    if os.path.exists(args.results):
Pierre-Yves Chibon 1e58be
        if not args.force:
Pierre-Yves Chibon 1e58be
            print(
Pierre-Yves Chibon 1e58be
                "Results folder exists, please remove it so we do not clobber"
Pierre-Yves Chibon 1e58be
                " or use --force"
Pierre-Yves Chibon 1e58be
            )
Pierre-Yves Chibon 1e58be
            return 1
Pierre-Yves Chibon 1e58be
        else:
Pierre-Yves Chibon 1e58be
            shutil.rmtree(args.results)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    os.mkdir(args.results)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    print("Pre-flight checks passed")
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    suites = []
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    if args.failed_tests:
Pierre-Yves Chibon 1e58be
        here = os.path.join(os.path.dirname(os.path.abspath(__file__)))
Pierre-Yves Chibon 1e58be
        failed_tests_fullpath = os.path.join(here, args.failed_tests)
Pierre-Yves Chibon 1e58be
        if not os.path.exists(failed_tests_fullpath):
Pierre-Yves Chibon 1e58be
            print("Could not find the specified file:%s" % failed_tests_fullpath)
Pierre-Yves Chibon 1e58be
            return 1
Pierre-Yves Chibon 1e58be
        print("Loading failed tests")
Pierre-Yves Chibon 1e58be
        try:
Pierre-Yves Chibon 1e58be
            with open(failed_tests_fullpath, "r") as ffile:
Pierre-Yves Chibon 1e58be
                suites = json.loads(ffile.read())
Pierre-Yves Chibon 1e58be
        except json.decoder.JSONDecodeError:
Pierre-Yves Chibon 1e58be
            bname = os.path.basename(args.failed_tests)
Pierre-Yves Chibon 1e58be
            if bname.endswith(".py") and bname.startswith("test_"):
Pierre-Yves Chibon 1e58be
                suites.append(bname.replace(".py", ""))
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    if len(suites) == 0:
Pierre-Yves Chibon 1e58be
        print("Loading all tests")
Pierre-Yves Chibon b75122
        for fname in os.listdir("./tests"):
Pierre-Yves Chibon 1e58be
            if not fname.endswith(".py"):
Pierre-Yves Chibon 1e58be
                continue
Pierre-Yves Chibon 1e58be
            if not fname.startswith("test_"):
Pierre-Yves Chibon 1e58be
                continue
Pierre-Yves Chibon 1e58be
            suites.append(fname.replace(".py", ""))
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 585ea1
    return _run_test_suites(args, suites)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
def do_rerun(args):
Pierre-Yves Chibon 1e58be
    """ Re-run tests that failed the last/specified run.
Pierre-Yves Chibon 1e58be
    """
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    # Some pre-flight checks
Pierre-Yves Chibon b75122
    if not os.path.exists("./.git") or not os.path.exists("./pagure"):
Pierre-Yves Chibon 1e58be
        print("Please run from a single level into the Pagure codebase")
Pierre-Yves Chibon 1e58be
        return 1
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    if not os.path.exists(args.results):
Pierre-Yves Chibon 1e58be
        print("Could not find an existing results folder at: %s" % args.results)
Pierre-Yves Chibon 1e58be
        return 1
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    if not os.path.exists(os.path.join(args.results, "newfailed")):
Pierre-Yves Chibon 1e58be
        print(
Pierre-Yves Chibon 1e58be
            "Could not find an failed tests in the results folder at: %s" % args.results
Pierre-Yves Chibon 1e58be
        )
Pierre-Yves Chibon 1e58be
        return 1
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    print("Pre-flight checks passed")
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    suites = []
Pierre-Yves Chibon 1e58be
    tmp = []
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    print("Loading failed tests")
Pierre-Yves Chibon 1e58be
    try:
Pierre-Yves Chibon 1e58be
        with open(os.path.join(args.results, "newfailed"), "r") as ffile:
Pierre-Yves Chibon 1e58be
            tmp = json.loads(ffile.read())
Pierre-Yves Chibon 1e58be
    except json.decoder.JSONDecodeError:
Pierre-Yves Chibon 1e58be
        print("File containing the failed tests is not JSON")
Pierre-Yves Chibon 1e58be
        return 1
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    for suite in tmp:
Pierre-Yves Chibon 1e58be
        if suite.startswith(("py2-", "py3-")):
Pierre-Yves Chibon 1e58be
            suites.append(suite[4:])
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 585ea1
    return _run_test_suites(args, set(suites))
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 84e668
def _get_pyvers(args):
Pierre-Yves Chibon 84e668
    pyvers = [2, 3]
Pierre-Yves Chibon 84e668
    if args.py2:
Pierre-Yves Chibon 84e668
        pyvers = [2,]
Pierre-Yves Chibon 84e668
    elif args.py3:
Pierre-Yves Chibon 84e668
        pyvers = [3,]
Pierre-Yves Chibon 84e668
Pierre-Yves Chibon 84e668
    un_versioned = False
Pierre-Yves Chibon 84e668
    try:
Pierre-Yves Chibon 84e668
        subprocess.check_call(["which", RUNNER_PY])
Pierre-Yves Chibon 84e668
        un_versioned = True
Pierre-Yves Chibon 84e668
    except subprocess.CalledProcessError:
Pierre-Yves Chibon 84e668
        print("No %s found  no unversioned runner" % RUNNER_PY)
Pierre-Yves Chibon 84e668
Pierre-Yves Chibon 84e668
    if 2 in pyvers:
Pierre-Yves Chibon 84e668
        nopy2 = False
Pierre-Yves Chibon 84e668
        try:
Pierre-Yves Chibon 84e668
            subprocess.check_call(["which", RUNNER_PY2])
Pierre-Yves Chibon 84e668
        except subprocess.CalledProcessError:
Pierre-Yves Chibon 84e668
            print("No %s found, removing python 2" % RUNNER_PY2)
Pierre-Yves Chibon 84e668
            del pyvers[pyvers.index(2)]
Pierre-Yves Chibon 84e668
Pierre-Yves Chibon 84e668
    if 3 in pyvers:
Pierre-Yves Chibon 84e668
        nopy3 = False
Pierre-Yves Chibon 84e668
        try:
Pierre-Yves Chibon 84e668
            subprocess.check_call(["which", RUNNER_PY3])
Pierre-Yves Chibon 84e668
        except subprocess.CalledProcessError:
Pierre-Yves Chibon 84e668
            print("No %s found, removing python 3" % RUNNER_PY3)
Pierre-Yves Chibon 84e668
            del pyvers[pyvers.index(3)]
Pierre-Yves Chibon 84e668
Pierre-Yves Chibon 84e668
    if not pyvers and un_versioned:
Pierre-Yves Chibon 84e668
        pyvers = [""]
Pierre-Yves Chibon 84e668
Pierre-Yves Chibon 84e668
    return pyvers
Pierre-Yves Chibon 84e668
Pierre-Yves Chibon 84e668
Pierre-Yves Chibon 1e58be
def _run_test_suites(args, suites):
Pierre-Yves Chibon 1e58be
    print("Using %d processes" % NUMPROCS)
Pierre-Yves Chibon 1e58be
    print("Start timing")
Pierre-Yves Chibon 1e58be
    start = time.time()
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    global PRINTLOCK
Pierre-Yves Chibon 1e58be
    PRINTLOCK = threading.RLock()
Pierre-Yves Chibon 1e58be
    global NUMREMAINING
Pierre-Yves Chibon 1e58be
    NUMREMAINING = 0
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    sem = threading.BoundedSemaphore(NUMPROCS)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    # Create a worker per test
Pierre-Yves Chibon 1e58be
    workers = {}
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 84e668
    pyvers = _get_pyvers(args)
Pierre-Yves Chibon 678713
Pierre-Yves Chibon c04da2
    if not pyvers:
Pierre-Yves Chibon 84e668
        return 1
Pierre-Yves Chibon 678713
Pierre-Yves Chibon 1e58be
    for suite in suites:
Pierre-Yves Chibon 1e58be
        for pyver in pyvers:
Pierre-Yves Chibon 1e58be
            NUMREMAINING += 1
Pierre-Yves Chibon 05cd04
            workers["py%s-%s" % (pyver, suite)] = WorkerThread(
Pierre-Yves Chibon 1e58be
                sem, pyver, suite, args.results, args.with_coverage
Pierre-Yves Chibon 1e58be
            )
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    # Start the workers
Pierre-Yves Chibon 1e58be
    print("Starting the workers")
Pierre-Yves Chibon 1e58be
    print()
Pierre-Yves Chibon 1e58be
    print()
Pierre-Yves Chibon 1e58be
    for worker in workers.values():
Pierre-Yves Chibon 1e58be
        worker.start()
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    # Wait for them to terminate
Pierre-Yves Chibon 1e58be
    for worker in workers:
Pierre-Yves Chibon 1e58be
        workers[worker].join()
Pierre-Yves Chibon 1e58be
    print_running()
Pierre-Yves Chibon 1e58be
    print()
Pierre-Yves Chibon 1e58be
    print("All work done")
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    # Gather results
Pierre-Yves Chibon 1e58be
    print()
Pierre-Yves Chibon 1e58be
    print()
Pierre-Yves Chibon 1e58be
    print("Failed tests:")
Pierre-Yves Chibon 1e58be
    for worker in workers:
Pierre-Yves Chibon 1e58be
        if not workers[worker].failed:
Pierre-Yves Chibon 1e58be
            continue
Pierre-Yves Chibon 1e58be
        print("FAILED test: %s" % (worker))
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    # Write failed
Pierre-Yves Chibon 1e58be
    if FAILED:
Pierre-Yves Chibon 1e58be
        with open(os.path.join(args.results, "newfailed"), "w") as ffile:
Pierre-Yves Chibon 1e58be
            ffile.write(json.dumps(FAILED))
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    # Stats
Pierre-Yves Chibon 1e58be
    end = time.time()
Pierre-Yves Chibon 1e58be
    print()
Pierre-Yves Chibon 1e58be
    print()
Pierre-Yves Chibon 1e58be
    print(
Pierre-Yves Chibon 1e58be
        "Ran %d tests in %f seconds, of which %d failed"
Pierre-Yves Chibon 1e58be
        % (len(workers), (end - start), len(FAILED))
Pierre-Yves Chibon 1e58be
    )
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    # Exit
Pierre-Yves Chibon 1e58be
    if len(FAILED) == 0:
Pierre-Yves Chibon 1e58be
        print("ALL PASSED! CONGRATULATIONS!")
Pierre-Yves Chibon 1e58be
    else:
Pierre-Yves Chibon 1e58be
        return 1
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    if args.with_coverage:
Pierre-Yves Chibon 1e58be
        do_show_coverage(args)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    return 0
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
def do_list(args):
Pierre-Yves Chibon 1e58be
    """ List tests that failed the last/specified run.
Pierre-Yves Chibon 1e58be
    """
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    # Some pre-flight checks
Pierre-Yves Chibon b75122
    if not os.path.exists("./.git") or not os.path.exists("./pagure"):
Pierre-Yves Chibon 1e58be
        print("Please run from a single level into the Pagure codebase")
Pierre-Yves Chibon 1e58be
        return 1
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    if not os.path.exists(args.results):
Pierre-Yves Chibon 1e58be
        print("Could not find an existing results folder at: %s" % args.results)
Pierre-Yves Chibon 1e58be
        return 1
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    if not os.path.exists(os.path.join(args.results, "newfailed")):
Pierre-Yves Chibon 1e58be
        print(
Pierre-Yves Chibon 1e58be
            "Could not find an failed tests in the results folder at: %s" % args.results
Pierre-Yves Chibon 1e58be
        )
Pierre-Yves Chibon 1e58be
        return 1
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    print("Pre-flight checks passed")
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    suites = []
Pierre-Yves Chibon 1e58be
    tmp = []
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    print("Loading failed tests")
Pierre-Yves Chibon 1e58be
    try:
Pierre-Yves Chibon 1e58be
        with open(os.path.join(args.results, "newfailed"), "r") as ffile:
Pierre-Yves Chibon 1e58be
            suites = json.loads(ffile.read())
Pierre-Yves Chibon 1e58be
    except json.decoder.JSONDecodeError:
Pierre-Yves Chibon 1e58be
        print("File containing the failed tests is not JSON")
Pierre-Yves Chibon 1e58be
        return 1
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    print("Failed tests")
Pierre-Yves Chibon 1e58be
    failed_tests = len(suites)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    if args.n:
Pierre-Yves Chibon 1e58be
        suites = suites[:args.n]
Pierre-Yves Chibon 1e58be
    print("- " + "\n- ".join(suites))
Pierre-Yves Chibon 1e58be
    print("Total: %s test failed" % failed_tests)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    if args.show:
Pierre-Yves Chibon 1e58be
        for suite in suites:
Pierre-Yves Chibon 1e58be
            cmd = ["less", os.path.join(args.results, suite)]
Pierre-Yves Chibon 1e58be
            subprocess.check_call(cmd)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
def do_show_coverage(args):
Pierre-Yves Chibon 1e58be
    print()
Pierre-Yves Chibon 1e58be
    print("Combining coverage results...")
Pierre-Yves Chibon 84e668
Pierre-Yves Chibon 84e668
    pyvers = _get_pyvers(args)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    for pyver in pyvers:
Pierre-Yves Chibon 1e58be
        coverfiles = []
Pierre-Yves Chibon 1e58be
        for fname in os.listdir(args.results):
Pierre-Yves Chibon 05cd04
            if fname.endswith(".coverage") and fname.startswith("py%s-" % pyver):
Pierre-Yves Chibon 1e58be
                coverfiles.append(os.path.join(args.results, fname))
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
        cover = None
Pierre-Yves Chibon 1e58be
        if pyver == 2:
Pierre-Yves Chibon 1e58be
            cover = COVER_PY2
Pierre-Yves Chibon 1e58be
        elif pyver == 3:
Pierre-Yves Chibon 1e58be
            cover = COVER_PY3
Pierre-Yves Chibon 84e668
        else:
Pierre-Yves Chibon 84e668
            cover = COVER_PY
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
        env = {"COVERAGE_FILE": os.path.join(args.results, "combined.coverage")}
Pierre-Yves Chibon 1e58be
        cmd = [cover, "combine"] + coverfiles
Pierre-Yves Chibon 1e58be
        subprocess.check_call(cmd, env=env)
Pierre-Yves Chibon 1e58be
        print()
Pierre-Yves Chibon 05cd04
        print("Python %s coverage: " % pyver)
Pierre-Yves Chibon b75122
        cmd = [cover, "report", "--include=./pagure/*"]
Pierre-Yves Chibon 1e58be
        subprocess.check_call(cmd, env=env)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
def main():
Pierre-Yves Chibon 1e58be
    """ Main function """
Pierre-Yves Chibon 1e58be
    # Set up parser for global args
Pierre-Yves Chibon 1e58be
    parser = setup_parser()
Pierre-Yves Chibon 1e58be
    # Parse the commandline
Pierre-Yves Chibon 1e58be
    try:
Pierre-Yves Chibon 1e58be
        arg = parser.parse_args()
Pierre-Yves Chibon 1e58be
    except argparse.ArgumentTypeError as err:
Pierre-Yves Chibon 1e58be
        print("\nError: {0}".format(err))
Pierre-Yves Chibon 1e58be
        return 2
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    logging.basicConfig()
Pierre-Yves Chibon 1e58be
    if arg.debug:
Pierre-Yves Chibon 1e58be
        LOG.setLevel(logging.DEBUG)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    if "func" not in arg:
Pierre-Yves Chibon 1e58be
        parser.print_help()
Pierre-Yves Chibon 1e58be
        return 1
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    arg.results = os.path.abspath(arg.results)
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    return_code = 0
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    try:
Pierre-Yves Chibon 1e58be
        return_code = arg.func(arg)
Pierre-Yves Chibon 1e58be
    except KeyboardInterrupt:
Pierre-Yves Chibon 1e58be
        print("\nInterrupted by user.")
Pierre-Yves Chibon 1e58be
        return_code = 1
Pierre-Yves Chibon 1e58be
    except Exception as err:
Pierre-Yves Chibon 1e58be
        print("Error: {0}".format(err))
Pierre-Yves Chibon 1e58be
        logging.exception("Generic error caught:")
Pierre-Yves Chibon 1e58be
        return_code = 5
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
    return return_code
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
Pierre-Yves Chibon 1e58be
if __name__ == "__main__":
Pierre-Yves Chibon 1e58be
    sys.exit(main())