tests.py 9.9 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
    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),
37
    make_option("--pdb", action="store_true", help="Drop into debugger on failures or errors"),
38 39 40 41 42 43
    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."
    ),
44
], share_with=['pavelib.utils.test.utils.clean_reports_dir'])
45 46 47 48 49 50 51 52 53 54 55
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),
56
        'verbosity': getattr(options, 'verbosity', 1),
57 58
        'extra_args': getattr(options, 'extra_args', ''),
        'cov_args': getattr(options, 'cov_args', ''),
59
        'skip_clean': getattr(options, 'skip_clean', False),
60
        'pdb': getattr(options, 'pdb', False),
61
        'disable_migrations': getattr(options, 'disable_migrations', False),
62 63 64 65 66
    }

    if test_id:
        if not system:
            system = test_id.split('/')[0]
67
        if system in ['common', 'openedx']:
68
            system = 'lms'
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
        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"),
92 93
    ('extra_args=', 'e', 'adds as extra args to the test command'),
    ('cov_args=', 'c', 'adds as args to coverage for the test run'),
94
    ('skip_clean', 'C', 'skip cleaning repository before running tests'),
95 96 97
    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),
98
    make_option("--pdb", action="store_true", help="Drop into debugger on failures or errors"),
99
], share_with=['pavelib.utils.test.utils.clean_reports_dir'])
100 101
def test_lib(options):
    """
102
    Run tests for common/lib/ and pavelib/ (paver-tests)
103 104 105 106 107 108 109
    """
    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),
110
        'verbosity': getattr(options, 'verbosity', 1),
111 112
        'extra_args': getattr(options, 'extra_args', ''),
        'cov_args': getattr(options, 'cov_args', ''),
113
        'skip_clean': getattr(options, 'skip_clean', False),
114
        'pdb': getattr(options, 'pdb', False),
115 116 117
    }

    if test_id:
118 119 120 121
        if '/' in test_id:
            lib = '/'.join(test_id.split('/')[0:3])
        else:
            lib = 'common/lib/' + test_id.split('.')[0]
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
        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"),
139 140
    ('extra_args=', 'e', 'adds as extra args to the test command'),
    ('cov_args=', 'c', 'adds as args to coverage for the test run'),
141 142 143
    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),
144
    make_option("--pdb", action="store_true", help="Drop into debugger on failures or errors"),
145 146 147 148 149 150
    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."
    ),
151 152 153 154 155 156 157 158
])
def test_python(options):
    """
    Run all python tests
    """
    opts = {
        'failed_only': getattr(options, 'failed', None),
        'fail_fast': getattr(options, 'fail_fast', None),
159
        'verbosity': getattr(options, 'verbosity', 1),
160 161
        'extra_args': getattr(options, 'extra_args', ''),
        'cov_args': getattr(options, 'cov_args', ''),
162
        'pdb': getattr(options, 'pdb', False),
163
        'disable_migrations': getattr(options, 'disable_migrations', False),
164 165 166 167 168 169 170 171 172 173 174
    }

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


@task
@needs(
    'pavelib.prereqs.install_prereqs',
    'pavelib.utils.test.utils.clean_reports_dir',
)
175
@cmdopts([
176 177 178
    ("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'),
179 180 181
    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),
182
    make_option("--pdb", action="store_true", help="Drop into debugger on failures or errors"),
183 184
])
def test(options):
185 186 187
    """
    Run all tests
    """
188
    opts = {
189 190 191
        'verbosity': getattr(options, 'verbosity', 1),
        'extra_args': getattr(options, 'extra_args', ''),
        'cov_args': getattr(options, 'cov_args', ''),
192
        'pdb': getattr(options, 'pdb', False),
193
    }
194
    # Subsuites to be added to the main suite
195
    python_suite = suites.PythonTestSuite('Python Tests', **opts)
196 197 198
    js_suite = suites.JsTestSuite('JS Tests', mode='run', with_coverage=True)

    # Main suite to be run
199
    all_unittests_suite = suites.TestSuite('All Tests', subsuites=[js_suite, python_suite])
200 201 202 203 204
    all_unittests_suite.run()


@task
@needs('pavelib.prereqs.install_prereqs')
205
@cmdopts([
206
    ("compare_branch=", "b", "Branch to compare against, defaults to origin/master"),
207 208
])
def coverage(options):
209 210 211
    """
    Build the html, xml, and diff coverage reports
    """
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
    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
231

232 233 234 235
    # Generate the coverage.py XML report
    sh("coverage xml --rcfile={}".format(rcfile))
    # Generate the coverage.py HTML report
    sh("coverage html --rcfile={}".format(rcfile))
236 237 238
    call_task('diff_coverage', options=dict(options))


239 240
@task
@needs('pavelib.prereqs.install_prereqs')
241 242 243 244 245 246 247 248 249
@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')

250 251 252 253
    # Find all coverage XML files (both Python and JavaScript)
    xml_reports = []

    for filepath in Env.REPORT_DIR.walk():
254
        if bool(re.match(r'^coverage.*\.xml$', filepath.basename())):
255 256 257 258 259
            xml_reports.append(filepath)

    if not xml_reports:
        err_msg = colorize(
            'red',
260 261
            "No coverage info found.  Run `paver test` before running "
            "`paver coverage`.\n"
262 263 264 265 266 267 268
        )
        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)
269 270 271 272 273 274 275 276 277
        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,
            )
        )

278
        print "\n"