diff options
Diffstat (limited to 'tasks/test.py')
-rw-r--r-- | tasks/test.py | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/tasks/test.py b/tasks/test.py new file mode 100644 index 0000000..f1c4991 --- /dev/null +++ b/tasks/test.py @@ -0,0 +1,207 @@ +# -*- coding: UTF-8 -*- +""" +Invoke test tasks. +""" + +from __future__ import print_function +import os.path +import sys +from invoke import task, Collection + +# -- TASK-LIBRARY: +from ._tasklet_cleanup import cleanup_tasks, cleanup_dirs, cleanup_files + + +# --------------------------------------------------------------------------- +# CONSTANTS: +# --------------------------------------------------------------------------- +USE_BEHAVE = False + + +# --------------------------------------------------------------------------- +# TASKS +# --------------------------------------------------------------------------- +@task(name="all", help={ + "args": "Command line args for test run.", +}) +def test_all(ctx, args="", options=""): + """Run all tests (default).""" + pytest_args = select_by_prefix(args, ctx.pytest.scopes) + behave_args = None + if USE_BEHAVE: + behave_args = select_by_prefix(args, ctx.behave_test.scopes) + pytest_should_run = not args or (args and pytest_args) + behave_should_run = not args or (args and behave_args) + if pytest_should_run: + pytest(ctx, pytest_args, options=options) + if behave_should_run and USE_BEHAVE: + behave(ctx, behave_args, options=options) + + +@task +def clean(ctx, dry_run=False): + """Cleanup (temporary) test artifacts.""" + directories = ctx.test.clean.directories or [] + files = ctx.test.clean.files or [] + cleanup_dirs(directories, dry_run=dry_run) + cleanup_files(files, dry_run=dry_run) + + +@task(name="unit") +def unittest(ctx, args="", options=""): + """Run unit tests.""" + pytest(ctx, args, options) + + +@task +def pytest(ctx, args="", options=""): + """Run unit tests.""" + args = args or ctx.pytest.args + options = options or ctx.pytest.options + ctx.run("pytest {options} {args}".format(options=options, args=args)) + + +@task(help={ + "args": "Command line args for behave", + "format": "Formatter to use (progress, pretty, ...)", +}) +def behave(ctx, args="", format="", options=""): + """Run behave tests.""" + format = format or ctx.behave_test.format + options = options or ctx.behave_test.options + args = args or ctx.behave_test.args + if os.path.exists("bin/behave"): + behave_cmd = "{python} bin/behave".format(python=sys.executable) + else: + behave_cmd = "{python} -m behave".format(python=sys.executable) + + for group_args in grouped_by_prefix(args, ctx.behave_test.scopes): + ctx.run("{behave} -f {format} {options} {args}".format( + behave=behave_cmd, format=format, options=options, args=group_args)) + + +@task(help={ + "args": "Tests to run (empty: all)", + "report": "Coverage report format to use (report, html, xml)", +}) +def coverage(ctx, args="", report="report", append=False): + """Determine test coverage (run pytest, behave)""" + append = append or ctx.coverage.append + report_formats = ctx.coverage.report_formats or [] + if report not in report_formats: + report_formats.insert(0, report) + opts = [] + if append: + opts.append("--append") + + pytest_args = select_by_prefix(args, ctx.pytest.scopes) + behave_args = select_by_prefix(args, ctx.behave_test.scopes) + pytest_should_run = not args or (args and pytest_args) + behave_should_run = not args or (args and behave_args) and USE_BEHAVE + if not args: + behave_args = ctx.behave_test.args or "features" + if isinstance(pytest_args, list): + pytest_args = " ".join(pytest_args) + if isinstance(behave_args, list): + behave_args = " ".join(behave_args) + + # -- RUN TESTS WITH COVERAGE: + if pytest_should_run: + ctx.run("coverage run {options} -m pytest {args}".format( + args=pytest_args, options=" ".join(opts))) + if behave_should_run and USE_BEHAVE: + behave_options = ctx.behave_test.coverage_options or "" + os.environ["COVERAGE_PROCESS_START"] = os.path.abspath(".coveragerc") + behave(ctx, args=behave_args, options=behave_options) + del os.environ["COVERAGE_PROCESS_START"] + + # -- POST-PROCESSING: + ctx.run("coverage combine") + for report_format in report_formats: + ctx.run("coverage {report_format}".format(report_format=report_format)) + + +# --------------------------------------------------------------------------- +# UTILITIES: +# --------------------------------------------------------------------------- +def select_prefix_for(arg, prefixes): + for prefix in prefixes: + if arg.startswith(prefix): + return prefix + return os.path.dirname(arg) + + +def select_by_prefix(args, prefixes): + selected = [] + for arg in args.strip().split(): + assert not arg.startswith("-"), "REQUIRE: arg, not options" + scope = select_prefix_for(arg, prefixes) + if scope: + selected.append(arg) + return " ".join(selected) + + +def grouped_by_prefix(args, prefixes): + """Group behave args by (directory) scope into multiple test-runs.""" + group_args = [] + current_scope = None + for arg in args.strip().split(): + assert not arg.startswith("-"), "REQUIRE: arg, not options" + scope = select_prefix_for(arg, prefixes) + if scope != current_scope: + if group_args: + # -- DETECTED GROUP-END: + yield " ".join(group_args) + group_args = [] + current_scope = scope + group_args.append(arg) + if group_args: + yield " ".join(group_args) + + +# --------------------------------------------------------------------------- +# TASK MANAGEMENT / CONFIGURATION +# --------------------------------------------------------------------------- +namespace = Collection(clean, unittest, pytest, coverage) +namespace.add_task(test_all, default=True) +if USE_BEHAVE: + namespace.add_task(behave) + +namespace.configure({ + "test": { + "clean": { + "directories": [ + ".cache", "assets", # -- TEST RUNS + # -- BEHAVE-SPECIFIC: + "__WORKDIR__", "reports", "test_results", + ], + "files": [ + ".coverage", ".coverage.*", + # -- BEHAVE-SPECIFIC: + "report.html", + "rerun*.txt", "rerun*.featureset", "testrun*.json", + ], + }, + }, + "pytest": { + "scopes": ["tests"], + "args": "", + "options": "", # -- NOTE: Overide in configfile "invoke.yaml" + }, + # "behave_test": behave.namespace._configuration["behave_test"], + "behave_test": { + "scopes": ["features"], + "args": "features", + "format": "progress", + "options": "", # -- NOTE: Overide in configfile "invoke.yaml" + "coverage_options": "", + }, + "coverage": { + "append": False, + "report_formats": ["report", "html"], + }, +}) + +# -- ADD CLEANUP TASK: +cleanup_tasks.add_task(clean, "clean_test") +cleanup_tasks.configure(namespace.configuration()) |