diff --git a/test/assertions_test.py b/test/assertions_test.py index 8e8001ea..dff308da 100644 --- a/test/assertions_test.py +++ b/test/assertions_test.py @@ -956,6 +956,7 @@ def three_warnings_caught(warnings): class DocTest(DocTestCase): module = assertions + if __name__ == '__main__': run() # vim:et:sts=4:sw=4: diff --git a/test/discovery_failure_test.py b/test/discovery_failure_test.py index 9f2172da..a0c6de99 100644 --- a/test/discovery_failure_test.py +++ b/test/discovery_failure_test.py @@ -23,7 +23,9 @@ def test_discover_test_with_broken_import(self): non-existent module is discovered.""" stdout, stderr = cmd_output( - 'python', '-m', 'testify.test_program', 'discovery_error', cwd='examples', + 'python', '-W', 'ignore::RuntimeWarning:runpy:', + '-m', 'testify.test_program', 'discovery_error', + cwd='examples', ) T.assert_equal( @@ -37,7 +39,7 @@ def test_discover_test_with_broken_import(self): r' submod = __import__\(module_name, fromlist=\[str\(\'__trash\'\)\]\)\n' r' File "[^"]+", line \d+, in \n' r' import non_existent_module\n' - r' ImportError: No module named \'?non_existent_module\'?\n' + r' (ImportError|ModuleNotFoundError): No module named \'?non_existent_module\'?\n' ), ) @@ -49,7 +51,7 @@ def test_discover_test_with_broken_import(self): r" submod = __import__\(module_name, fromlist=\[str\(\'__trash\'\)\]\)\n" r' File .+, line \d+, in \n' r' import non_existent_module\n' - r"ImportError: No module named '?non_existent_module'?\n" + r"(ImportError|ModuleNotFoundError): No module named '?non_existent_module'?\n" ), ) @@ -65,6 +67,7 @@ def test_discover_test_with_unknown_import_error(self): T.assert_in('AttributeError: aaaaa!', stderr) T.assert_in('AttributeError: aaaaa!', stdout) + if __name__ == '__main__': T.run() diff --git a/test/plugins/test_case_time_log_test.py b/test/plugins/test_case_time_log_test.py index fb63a64a..fcf512c2 100644 --- a/test/plugins/test_case_time_log_test.py +++ b/test/plugins/test_case_time_log_test.py @@ -30,6 +30,7 @@ def mock_conf_files(): with mock.patch.object(six.moves.builtins, 'open', _mock_conf_file_open): yield + start_time = datetime.datetime(2014, 12, 17, 12, 38, 37, 0) end_time = datetime.datetime(2014, 12, 17, 15, 38, 37, 0) output_str = ( diff --git a/testify/deprecated_assertions.py b/testify/deprecated_assertions.py index 19936b1e..447247c6 100644 --- a/testify/deprecated_assertions.py +++ b/testify/deprecated_assertions.py @@ -94,8 +94,10 @@ def failIfAlmostEqual(self, first, second, places=7, msg=None): # Synonyms for assertion methods + # stop using these assertEqual = assertEquals = failUnlessEqual + # stop using these assertNotEqual = assertNotEquals = failIfEqual diff --git a/testify/plugins/json_log.py b/testify/plugins/json_log.py index a97369a3..4ff727fe 100644 --- a/testify/plugins/json_log.py +++ b/testify/plugins/json_log.py @@ -72,6 +72,7 @@ def test_complete(self, result): self.log_file.write(json.dumps(result)) self.log_file.write("\n") + self.log_file.flush() self._reset_logging() diff --git a/testify/plugins/test_case_time_log.py b/testify/plugins/test_case_time_log.py index 653aeb72..7d5c5b5a 100644 --- a/testify/plugins/test_case_time_log.py +++ b/testify/plugins/test_case_time_log.py @@ -36,6 +36,7 @@ def _reset_logging(self): def test_case_complete(self, result): self.log_file.write(json.dumps(result)) self.log_file.write("\n") + self.log_file.flush() self._reset_logging() diff --git a/testify/test_case.py b/testify/test_case.py index 108893a1..251268d8 100644 --- a/testify/test_case.py +++ b/testify/test_case.py @@ -19,6 +19,7 @@ from collections import defaultdict import functools +import itertools import inspect import types import unittest @@ -133,7 +134,7 @@ def __init__(self, *args, **kwargs): self.__suites_exclude = kwargs.get('suites_exclude', set()) self.__suites_require = kwargs.get('suites_require', set()) - self.__name_overrides = kwargs.get('name_overrides', None) + self.__name_overrides = kwargs.get('name_overrides', itertools.repeat(None)) or itertools.repeat(None) TestResult.debug = kwargs.get('debugger') # sorry :( @@ -168,25 +169,37 @@ def runnable_test_methods(self): any of our exclude_suites. If there are any require_suites, it will then further limit itself to test methods in those suites. """ - for member_name in dir(self): - if not member_name.startswith("test"): - continue - member = getattr(self, member_name) - if not inspect.ismethod(member): - continue - - member_suites = self.suites(member) - - # if there are any exclude suites, exclude methods under them - if self.__suites_exclude and self.__suites_exclude & member_suites: - continue - # if there are any require suites, only run methods in *all* of those suites - if self.__suites_require and not ((self.__suites_require & member_suites) == self.__suites_require): - continue - - # if there are any name overrides, only run the named methods - if self.__name_overrides is None or member.__name__ in self.__name_overrides: - yield member + already_inspected = set() + name_overrides, self.__name_overrides = itertools.tee(self.__name_overrides) + + for name_override in name_overrides: + for member_name in dir(self): + # No overrides were passed in, stop if we've seen all members already + if not name_override: + if member_name in already_inspected: + return + already_inspected.add(member_name) + elif name_override == "__testify_abort_enumeration": + return + + if not member_name.startswith("test"): + continue + member = getattr(self, member_name) + if not inspect.ismethod(member): + continue + + member_suites = self.suites(member) + + # if there are any exclude suites, exclude methods under them + if self.__suites_exclude and self.__suites_exclude & member_suites: + continue + # if there are any require suites, only run methods in *all* of those suites + if self.__suites_require and not ((self.__suites_require & member_suites) == self.__suites_require): + continue + + # if there are any name overrides, only run the named methods + if name_override is None or member.__name__ == name_override: + yield member def run(self): """Delegator method encapsulating the flow for executing a TestCase instance. diff --git a/testify/test_rerunner.py b/testify/test_rerunner.py index b7fae8e4..f35fbbbe 100644 --- a/testify/test_rerunner.py +++ b/testify/test_rerunner.py @@ -1,4 +1,5 @@ from __future__ import absolute_import +from itertools import chain from itertools import groupby from json import loads import sys @@ -8,6 +9,15 @@ from .test_runner import TestRunner +def readlines(fileobj): + while True: + line = fileobj.readline().rstrip() + if line: + yield line + else: + return + + class Test(namedtuple('TestTuple', ('module', 'cls', 'method'))): @classmethod def from_json(cls, json): @@ -32,23 +42,29 @@ def discover(self): else: tests = open(self.filename) - with tests as tests: - tests = tests.read() + test = tests.readline().rstrip() + + if not test: + return - if tests.startswith('{'): + if test.startswith('{'): constructor = Test.from_json else: constructor = Test.from_name - tests = [ - constructor(test) - for test in tests.splitlines() - if test # Skip blank lines - ] + # Put back the test name we peeked at to choose a constructor + tests = chain( + [constructor(test)], + ( + constructor(test.rstrip()) + for test in readlines(tests) + if test # Skip blank lines + ), + ) for class_path, tests in groupby(tests, lambda test: test[:2]): - methods = [test.method for test in tests] module, cls = class_path + methods = (test.method for test in tests) cls = test_discovery.import_test_class(module, cls) yield self._construct_test(cls, name_overrides=methods) diff --git a/testify/test_runner_json_replay.py b/testify/test_runner_json_replay.py index 2f4a2a9a..76e5912d 100644 --- a/testify/test_runner_json_replay.py +++ b/testify/test_runner_json_replay.py @@ -58,7 +58,7 @@ def loadlines(self): continue try: results.append(json.loads(line.strip())) - except: + except Exception: sys.exit("Invalid JSON line: %r" % line.strip()) if lines[-1].strip() != "RUN COMPLETE":