suite.py 4.15 KB
Newer Older
1 2 3 4 5
"""
A class used for defining and running test suites
"""
import sys
import subprocess
6 7

from paver import tasks
8 9
from paver.easy import sh

10 11 12 13 14
from pavelib.utils.process import kill_process

try:
    from pygments.console import colorize
except ImportError:
15
    colorize = lambda color, text: text
16 17 18 19 20 21 22 23 24 25 26 27

__test__ = False  # do not collect


class TestSuite(object):
    """
    TestSuite is a class that defines how groups of tests run.
    """
    def __init__(self, *args, **kwargs):
        self.root = args[0]
        self.subsuites = kwargs.get('subsuites', [])
        self.failed_suites = []
28
        self.verbosity = int(kwargs.get('verbosity', 1))
29
        self.skip_clean = kwargs.get('skip_clean', False)
30
        self.passthrough_options = kwargs.get('passthrough_options', [])
31

32 33 34 35 36 37 38 39 40 41
    def __enter__(self):
        """
        This will run before the test suite is run with the run_suite_tests method.
        If self.run_test is called directly, it should be run in a 'with' block to
        ensure that the proper context is created.

        Specific setup tasks should be defined in each subsuite.

        i.e. Checking for and defining required directories.
        """
42
        print "\nSetting up for {suite_name}".format(suite_name=self.root)
43 44 45 46 47 48 49 50 51 52 53 54
        self.failed_suites = []

    def __exit__(self, exc_type, exc_value, traceback):
        """
        This is run after the tests run with the run_suite_tests method finish.
        Specific clean up tasks should be defined in each subsuite.

        If self.run_test is called directly, it should be run in a 'with' block
        to ensure that clean up happens properly.

        i.e. Cleaning mongo after the lms tests run.
        """
55
        print "\nCleaning up after {suite_name}".format(suite_name=self.root)
56 57 58 59 60 61 62 63 64 65 66 67 68 69

    @property
    def cmd(self):
        """
        The command to run tests (as a string). For this base class there is none.
        """
        return None

    def run_test(self):
        """
        Runs a self.cmd in a subprocess and waits for it to finish.
        It returns False if errors or failures occur. Otherwise, it
        returns True.
        """
70
        cmd = " ".join(self.cmd)
71 72 73 74 75

        if tasks.environment.dry_run:
            tasks.environment.info(cmd)
            return

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
        sys.stdout.write(cmd)

        msg = colorize(
            'green',
            '\n{bar}\n Running tests for {suite_name} \n{bar}\n'.format(suite_name=self.root, bar='=' * 40),
        )

        sys.stdout.write(msg)
        sys.stdout.flush()

        kwargs = {'shell': True, 'cwd': None}
        process = None

        try:
            process = subprocess.Popen(cmd, **kwargs)
            process.communicate()
        except KeyboardInterrupt:
            kill_process(process)
            sys.exit(1)
        else:
96
            return process.returncode == 0
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124

    def run_suite_tests(self):
        """
        Runs each of the suites in self.subsuites while tracking failures
        """
        # Uses __enter__ and __exit__ for context
        with self:
            # run the tests for this class, and for all subsuites
            if self.cmd:
                passed = self.run_test()
                if not passed:
                    self.failed_suites.append(self)

            for suite in self.subsuites:
                suite.run_suite_tests()
                if len(suite.failed_suites) > 0:
                    self.failed_suites.extend(suite.failed_suites)

    def report_test_results(self):
        """
        Writes a list of failed_suites to sys.stderr
        """
        if len(self.failed_suites) > 0:
            msg = colorize('red', "\n\n{bar}\nTests failed in the following suites:\n* ".format(bar="=" * 48))
            msg += colorize('red', '\n* '.join([s.root for s in self.failed_suites]) + '\n\n')
        else:
            msg = colorize('green', "\n\n{bar}\nNo test failures ".format(bar="=" * 48))

125
        print msg
126 127 128 129 130 131

    def run(self):
        """
        Runs the tests in the suite while tracking and reporting failures.
        """
        self.run_suite_tests()
132 133 134 135

        if tasks.environment.dry_run:
            return

136 137 138 139
        self.report_test_results()

        if len(self.failed_suites) > 0:
            sys.exit(1)