diff --git a/.gitignore b/.gitignore index 0ee4a86..dbeb5a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build .coverage htmlcov +.cache diff --git a/.travis.yml b/.travis.yml index 7786419..4c1477a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,6 @@ matrix: install: - pip install -r dev-requirements.txt script: - - TERM=xterm-256color inv test + - inv test # I have this in my git pre-push hook, but contributors probably don't - flake8 diff --git a/dev-requirements.txt b/dev-requirements.txt index c839e7b..9df19bb 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,15 +1,16 @@ # Dev version of Invoke invoke>=0.13.0,<2.0 -# Self, for runtime/task dependencies --e . # For packaging wheel==0.24 twine==1.5 # For testing -nose==1.3.0 -spec>=1.4.0,<2.0 +pytest-relaxed>=0.1.0,<2 +pytest-cov==2.4.0 mock==1.0.1 watchdog==0.8.3 coverage==3.7.1 # For linting flake8==2.4.0 + +# Self, for runtime/task dependencies +-e . diff --git a/invocations/pytest.py b/invocations/pytest.py new file mode 100644 index 0000000..8d9eec4 --- /dev/null +++ b/invocations/pytest.py @@ -0,0 +1,51 @@ +""" +Pytest-using variant of testing.py. Will eventually replace the latter. +""" + +from invoke import Collection, task + + +@task +def test(c, verbose=True, color=True, capture='sys', opts=''): + """ + Run pytest with given options. + + :param bool verbose: + Whether to run tests in verbose mode. + + :param bool color: + Whether to request colorized output (typically only works when + ``verbose=True``.) + + :param str capture: + What type of stdout/err capturing pytest should use. Defaults to + ``sys`` since pytest's own default, ``fd``, tends to trip up + subprocesses trying to detect PTY status. Can be set to ``no`` for no + capturing / useful print-debugging / etc. + + :param str opts: + Extra runtime options to hand to ``pytest``. + """ + # TODO: really need better tooling around these patterns + # TODO: especially the problem of wanting to be configurable, but + # sometimes wanting to override one's config via kwargs; and also needing + # non-None defaults in the kwargs to inform the parser (or have to + # configure it explicitly...?) + flags = [] + if verbose: + flags.append('--verbose') + if color: + flags.append('--color=yes') + flags.append('--capture={}'.format(capture)) + if opts is not None: + flags.append(opts) + c.run("pytest {}".format(" ".join(flags)), pty=True) + + +@task +def coverage(c): + """ + Run pytest with coverage enabled. + + Assumes the ``pytest-cov`` pytest plugin is installed. + """ diff --git a/setup.cfg b/setup.cfg index 455a3aa..f7e9484 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,3 +5,7 @@ universal = 1 exclude = .git,build,dist ignore = E124,E125,E128,E261,E301,E302,E303 max-line-length = 79 + +[tool:pytest] +testpaths = tests +python_files = * diff --git a/tasks.py b/tasks.py index aaec7cb..d77033b 100644 --- a/tasks.py +++ b/tasks.py @@ -1,28 +1,20 @@ from invoke import Collection, task from invocations.packaging import release -from invocations.testing import test, watch_tests, coverage as coverage_ +from invocations import pytest as pytests -# TODO: add coverage at the Travis level as well sometime. For now this is just -# to help me as I overhaul the release modules - -@task -def coverage(c, html=True): - # TODO: can we realistically make use of functools.partial for this sort of - # thing? - # TODO: is it best left to config option overrides (currently the usual - # approach)? Is there stuff we can do to make that even easier? - return coverage_(c, html=html, integration_=False) - - -ns = Collection(release, test, watch_tests, coverage) +ns = Collection(release, pytests.test, pytests.coverage) ns.configure({ - 'tests': { - 'package': 'invocations', - }, 'packaging': { 'sign': True, 'wheel': True, }, + 'run': { + 'env': { + # Our ANSI color tests test against hardcoded codes appropriate for + # this terminal, for now. + 'TERM': 'xterm-256color', + }, + }, }) diff --git a/tests/console.py b/tests/console.py index 6c0f7df..88b5ff7 100644 --- a/tests/console.py +++ b/tests/console.py @@ -8,7 +8,7 @@ from invocations.console import confirm -class confirm_(Spec): +class confirm_: @patch('invocations.console.input', return_value='yes') def displays_question_with_yes_no_suffix(self, mock_input): confirm("Are you sure?") diff --git a/tests/packaging/release.py b/tests/packaging/release.py index 4838493..cd8cc91 100644 --- a/tests/packaging/release.py +++ b/tests/packaging/release.py @@ -9,7 +9,9 @@ from invoke.vendor.lexicon import Lexicon from mock import Mock, patch -from spec import Spec, trap, skip, eq_, ok_, raises +# TODO: trap might work as-is but should still be migrated into pytest-relaxed +# (or find/use existing pytest functionality for same) +from spec import trap, skip, eq_, ok_, raises from invocations.packaging.semantic_version_monkey import Version @@ -22,7 +24,7 @@ ) -class release_line_(Spec): +class release_line_: def assumes_bugfix_if_release_branch(self): c = MockContext(run=Result("2.7")) eq_(release_line(c)[1], Release.BUGFIX) @@ -42,7 +44,7 @@ def is_undefined_if_specific_commit_checkout(self): eq_(release_line(c)[1], Release.UNDEFINED) -class latest_feature_bucket_(Spec): +class latest_feature_bucket_: def base_case_of_single_release_family(self): eq_( latest_feature_bucket(dict.fromkeys(['unreleased_1_feature'])), @@ -74,7 +76,7 @@ def ordering_goes_by_numeric_not_lexical_order(self): ) -class release_and_issues_(Spec): +class release_and_issues_: class bugfix: # TODO: factor out into setup() so each test has some excluded/ignored # data in it - helps avoid naive implementation returning x[0] etc. @@ -116,7 +118,7 @@ def undefined_always_returns_None_and_empty_list(self): skip() -class find_package_(Spec): +class find_package_: def can_be_short_circuited_with_config_value(self): # TODO: should we just bundle this + the version part into one # function and setting? do we ever peep into the package for anything @@ -137,7 +139,7 @@ def errors_if_ambiguous_results(self): skip() -class load_version_(Spec): +class load_version_: def setup(self): sys.path.insert(0, support_dir) @@ -167,7 +169,7 @@ def errors_usefully_if_version_module_not_found(self): skip() -class latest_and_next_version_(Spec): +class latest_and_next_version_: def next_patch_of_bugfix_release(self): eq_( latest_and_next_version(Lexicon({ @@ -313,7 +315,7 @@ def _expect_actions(self, *actions): ) -class status_(Spec): +class status_: class overall_behavior: _branch = '1.1' _changelog = 'unreleased_1.1_bugs' @@ -370,6 +372,9 @@ def returns_lexica_for_reuse(self): eq_(found_state.latest_version, Version('1.1.1')) eq_(found_state.tags, [Version(x) for x in self._tags]) + # TODO: I got this attribute jazz working in pytest but see if there is a + # 'native' pytest feature that works better (while still in conjunction + # with nested tasks, ideally) class release_line_branch: _branch = '1.1' @@ -572,7 +577,7 @@ def _run_all(c, mute=True): raise -class All(Spec): +class All: "all_" # mehhh # NOTE: just testing the base case of 'everything needs updating', @@ -700,7 +705,7 @@ def no_changelog_update_needed_means_no_changelog_edit(self, _): # TODO: yes, when I personally went from TERM=xterm-256color to # TERM=screen-256color, that made these tests break! Updating test machinery to # account for now, but...not ideal! -class component_state_enums_contain_human_readable_values(Spec): +class component_state_enums_contain_human_readable_values: class changelog: def okay(self): eq_(