tests.py 10.3 KB
Newer Older
1 2 3
"""
Unit test tasks
"""
4
import re
5 6
import os
import sys
7
from paver.easy import sh, task, cmdopts, needs, call_task
8 9
from pavelib.utils.test import suites
from pavelib.utils.envs import Env
10
from optparse import make_option
11 12 13 14

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 28

__test__ = False  # do not collect


@task
@needs(
    'pavelib.prereqs.install_prereqs',
    'pavelib.utils.test.utils.clean_reports_dir',
)
@cmdopts([
    ("system=", "s", "System to act on"),
    ("test_id=", "t", "Test id"),
    ("failed", "f", "Run only failed tests"),
29
    ("fail_fast", "x", "Fail suite on first failed test"),
30
    ("fasttest", "a", "Run without collectstatic"),
31 32
    ('extra_args=', 'e', 'adds as extra args to the test command'),
    ('cov_args=', 'c', 'adds as args to coverage for the test run'),
33
    ('skip_clean', 'C', 'skip cleaning repository before running tests'),
34 35 36
    ('processes=', 'p', 'number of processes to use running tests'),
    make_option('-r', '--randomize', action='store_true', dest='randomize', help='run the tests in a random order'),
    make_option('--no-randomize', action='store_false', dest='randomize', help="don't run the tests in a random order"),
37 38 39
    make_option("--verbose", action="store_const", const=2, dest="verbosity"),
    make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"),
    make_option("-v", "--verbosity", action="count", dest="verbosity", default=1),
40
    make_option("--pdb", action="store_true", help="Drop into debugger on failures or errors"),
41 42 43 44 45 46
    make_option(
        '--disable-migrations',
        action='store_true',
        dest='disable_migrations',
        help="Create tables directly from apps' models. Can also be used by exporting DISABLE_MIGRATIONS=1."
    ),
47
], share_with=['pavelib.utils.test.utils.clean_reports_dir'])
48 49 50 51 52 53 54 55 56 57 58
def test_system(options):
    """
    Run tests on our djangoapps for lms and cms
    """
    system = getattr(options, 'system', None)
    test_id = getattr(options, 'test_id', None)

    opts = {
        'failed_only': getattr(options, 'failed', None),
        'fail_fast': getattr(options, 'fail_fast', None),
        'fasttest': getattr(options, 'fasttest', None),
59
        'verbosity': getattr(options, 'verbosity', 1),
60 61
        'extra_args': getattr(options, 'extra_args', ''),
        'cov_args': getattr(options, 'cov_args', ''),
62
        'skip_clean': getattr(options, 'skip_clean', False),
63
        'pdb': getattr(options, 'pdb', False),
64
        'disable_migrations': getattr(options, 'disable_migrations', False),
65 66
        'processes': getattr(options, 'processes', None),
        'randomize': getattr(options, 'randomize', None),
67 68 69 70 71
    }

    if test_id:
        if not system:
            system = test_id.split('/')[0]
72
        if system in ['common', 'openedx']:
73
            system = 'lms'
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
        opts['test_id'] = test_id

    if test_id or system:
        system_tests = [suites.SystemTestSuite(system, **opts)]
    else:
        system_tests = []
        for syst in ('cms', 'lms'):
            system_tests.append(suites.SystemTestSuite(syst, **opts))

    test_suite = suites.PythonTestSuite('python tests', subsuites=system_tests, **opts)
    test_suite.run()


@task
@needs(
    'pavelib.prereqs.install_prereqs',
    'pavelib.utils.test.utils.clean_reports_dir',
)
@cmdopts([
    ("lib=", "l", "lib to test"),
    ("test_id=", "t", "Test id"),
    ("failed", "f", "Run only failed tests"),
    ("fail_fast", "x", "Run only failed tests"),
97 98
    ('extra_args=', 'e', 'adds as extra args to the test command'),
    ('cov_args=', 'c', 'adds as args to coverage for the test run'),
99
    ('skip_clean', 'C', 'skip cleaning repository before running tests'),
100 101 102
    make_option("--verbose", action="store_const", const=2, dest="verbosity"),
    make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"),
    make_option("-v", "--verbosity", action="count", dest="verbosity", default=1),
103
    make_option("--pdb", action="store_true", help="Drop into debugger on failures or errors"),
104
], share_with=['pavelib.utils.test.utils.clean_reports_dir'])
105 106
def test_lib(options):
    """
107
    Run tests for common/lib/ and pavelib/ (paver-tests)
108 109 110 111 112 113 114
    """
    lib = getattr(options, 'lib', None)
    test_id = getattr(options, 'test_id', lib)

    opts = {
        'failed_only': getattr(options, 'failed', None),
        'fail_fast': getattr(options, 'fail_fast', None),
115
        'verbosity': getattr(options, 'verbosity', 1),
116 117
        'extra_args': getattr(options, 'extra_args', ''),
        'cov_args': getattr(options, 'cov_args', ''),
118
        'skip_clean': getattr(options, 'skip_clean', False),
119
        'pdb': getattr(options, 'pdb', False),
120 121 122
    }

    if test_id:
123 124 125 126
        if '/' in test_id:
            lib = '/'.join(test_id.split('/')[0:3])
        else:
            lib = 'common/lib/' + test_id.split('.')[0]
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
        opts['test_id'] = test_id
        lib_tests = [suites.LibTestSuite(lib, **opts)]
    else:
        lib_tests = [suites.LibTestSuite(d, **opts) for d in Env.LIB_TEST_DIRS]

    test_suite = suites.PythonTestSuite('python tests', subsuites=lib_tests, **opts)
    test_suite.run()


@task
@needs(
    'pavelib.prereqs.install_prereqs',
    'pavelib.utils.test.utils.clean_reports_dir',
)
@cmdopts([
    ("failed", "f", "Run only failed tests"),
    ("fail_fast", "x", "Run only failed tests"),
144 145
    ('extra_args=', 'e', 'adds as extra args to the test command'),
    ('cov_args=', 'c', 'adds as args to coverage for the test run'),
146 147 148
    make_option("--verbose", action="store_const", const=2, dest="verbosity"),
    make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"),
    make_option("-v", "--verbosity", action="count", dest="verbosity", default=1),
149
    make_option("--pdb", action="store_true", help="Drop into debugger on failures or errors"),
150 151 152 153 154 155
    make_option(
        '--disable-migrations',
        action='store_true',
        dest='disable_migrations',
        help="Create tables directly from apps' models. Can also be used by exporting DISABLE_MIGRATIONS=1."
    ),
156 157 158 159 160 161 162 163
])
def test_python(options):
    """
    Run all python tests
    """
    opts = {
        'failed_only': getattr(options, 'failed', None),
        'fail_fast': getattr(options, 'fail_fast', None),
164
        'verbosity': getattr(options, 'verbosity', 1),
165 166
        'extra_args': getattr(options, 'extra_args', ''),
        'cov_args': getattr(options, 'cov_args', ''),
167
        'pdb': getattr(options, 'pdb', False),
168
        'disable_migrations': getattr(options, 'disable_migrations', False),
169 170 171 172 173 174 175 176 177 178 179
    }

    python_suite = suites.PythonTestSuite('Python Tests', **opts)
    python_suite.run()


@task
@needs(
    'pavelib.prereqs.install_prereqs',
    'pavelib.utils.test.utils.clean_reports_dir',
)
180
@cmdopts([
181 182 183
    ("suites", "s", "List of unit test suites to run. (js, lib, cms, lms)"),
    ('extra_args=', 'e', 'adds as extra args to the test command'),
    ('cov_args=', 'c', 'adds as args to coverage for the test run'),
184 185 186
    make_option("--verbose", action="store_const", const=2, dest="verbosity"),
    make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"),
    make_option("-v", "--verbosity", action="count", dest="verbosity", default=1),
187
    make_option("--pdb", action="store_true", help="Drop into debugger on failures or errors"),
188 189
])
def test(options):
190 191 192
    """
    Run all tests
    """
193
    opts = {
194 195 196
        'verbosity': getattr(options, 'verbosity', 1),
        'extra_args': getattr(options, 'extra_args', ''),
        'cov_args': getattr(options, 'cov_args', ''),
197
        'pdb': getattr(options, 'pdb', False),
198
    }
199
    # Subsuites to be added to the main suite
200
    python_suite = suites.PythonTestSuite('Python Tests', **opts)
201 202 203
    js_suite = suites.JsTestSuite('JS Tests', mode='run', with_coverage=True)

    # Main suite to be run
204
    all_unittests_suite = suites.TestSuite('All Tests', subsuites=[js_suite, python_suite])
205 206 207 208 209
    all_unittests_suite.run()


@task
@needs('pavelib.prereqs.install_prereqs')
210
@cmdopts([
211
    ("compare_branch=", "b", "Branch to compare against, defaults to origin/master"),
212 213
])
def coverage(options):
214 215 216
    """
    Build the html, xml, and diff coverage reports
    """
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
    report_dir = Env.REPORT_DIR
    rcfile = Env.PYTHON_COVERAGERC

    if not (report_dir / '.coverage').isfile():
        # This may be that the coverage files were generated using -p,
        # try to combine them to the one file that we need.
        sh("coverage combine --rcfile={}".format(rcfile))

    if not os.path.getsize(report_dir / '.coverage') > 50:
        # Check if the .coverage data file is larger than the base file,
        # because coverage combine will always at least make the "empty" data
        # file even when there isn't any data to be combined.
        err_msg = colorize(
            'red',
            "No coverage info found.  Run `paver test` before running "
            "`paver coverage`.\n"
        )
        sys.stderr.write(err_msg)
        return
236

237 238 239 240
    # Generate the coverage.py XML report
    sh("coverage xml --rcfile={}".format(rcfile))
    # Generate the coverage.py HTML report
    sh("coverage html --rcfile={}".format(rcfile))
241 242 243
    call_task('diff_coverage', options=dict(options))


244 245
@task
@needs('pavelib.prereqs.install_prereqs')
246 247 248 249 250 251 252 253 254
@cmdopts([
    ("compare_branch=", "b", "Branch to compare against, defaults to origin/master"),
])
def diff_coverage(options):
    """
    Build the diff coverage reports
    """
    compare_branch = getattr(options, 'compare_branch', 'origin/master')

255 256 257 258
    # Find all coverage XML files (both Python and JavaScript)
    xml_reports = []

    for filepath in Env.REPORT_DIR.walk():
259
        if bool(re.match(r'^coverage.*\.xml$', filepath.basename())):
260 261 262 263 264
            xml_reports.append(filepath)

    if not xml_reports:
        err_msg = colorize(
            'red',
265 266
            "No coverage info found.  Run `paver test` before running "
            "`paver coverage`.\n"
267 268 269 270 271 272 273
        )
        sys.stderr.write(err_msg)
    else:
        xml_report_str = ' '.join(xml_reports)
        diff_html_path = os.path.join(Env.REPORT_DIR, 'diff_coverage_combined.html')

        # Generate the diff coverage reports (HTML and console)
274 275 276 277 278 279 280 281 282
        sh(
            "diff-cover {xml_report_str} --compare-branch={compare_branch} "
            "--html-report {diff_html_path}".format(
                xml_report_str=xml_report_str,
                compare_branch=compare_branch,
                diff_html_path=diff_html_path,
            )
        )

283
        print "\n"