pytest (recode-3.7.4) | : | pytest (recode-3.7.5) | ||
---|---|---|---|---|
#!/usr/bin/env python | #!/usr/bin/env python3 | |||
# -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | |||
# Copyright © 2005 Progiciels Bourbeau-Pinard inc. | # Copyright © 2005 Progiciels Bourbeau-Pinard inc. | |||
# François Pinard <pinard@iro.umontreal.ca>, 2005. | # François Pinard <pinard@iro.umontreal.ca>, 2005. | |||
u"""\ | u"""\ | |||
Execute a validation suite built in pylib's py.test style. | Execute a validation suite built in pylib's py.test style. | |||
Usage: pytest [OPTION]... [PATH]... | Usage: pytest [OPTION]... [PATH]... | |||
Options: | Options: | |||
skipping to change at line 44 | skipping to change at line 44 | |||
-s is selected, failed tests are detailed on stdout and, if there is at | -s is selected, failed tests are detailed on stdout and, if there is at | |||
least one such failed test, the return status of this program is non-zero. | least one such failed test, the return status of this program is non-zero. | |||
""" | """ | |||
# This tool implements a minimal set of specifications stolen from the | # This tool implements a minimal set of specifications stolen from the | |||
# excellent Codespeak's py.test, at a time I really needed py.test to be | # excellent Codespeak's py.test, at a time I really needed py.test to be | |||
# more Unicode-aware. | # more Unicode-aware. | |||
__metaclass__ = type | __metaclass__ = type | |||
import inspect, os, sys, time, traceback | import inspect, os, sys, time, traceback | |||
from StringIO import StringIO | from io import StringIO | |||
# How many displayable characters in an output line. | # How many displayable characters in an output line. | |||
WIDTH = 79 | WIDTH = 79 | |||
class Limit_Reached(Exception): pass | class Limit_Reached(Exception): pass | |||
class Main: | class Main: | |||
prefix = 'test_' | prefix = 'test_' | |||
pattern = [] | pattern = [] | |||
exclusion = [] | exclusion = [] | |||
skipping to change at line 146 | skipping to change at line 146 | |||
except ImportError: | except ImportError: | |||
if self.save: | if self.save: | |||
self.failures.append(self.total_count + 1) | self.failures.append(self.total_count + 1) | |||
else: | else: | |||
tracing = StringIO() | tracing = StringIO() | |||
traceback.print_exc(file=tracing) | traceback.print_exc(file=tracing) | |||
self.failures.append( | self.failures.append( | |||
(self.total_count + 1, file_name, | (self.total_count + 1, file_name, | |||
None, None, | None, None, | |||
None, None, | None, None, | |||
str(tracing.getvalue()))) | tracing.getvalue())) | |||
else: | else: | |||
self.handle_module(file_name, module) | self.handle_module(file_name, module) | |||
finally: | finally: | |||
del sys.path[0] | del sys.path[0] | |||
if self.counter and not self.verbose: | if self.counter and not self.verbose: | |||
text = u'(%d)' % self.counter | text = u'(%d)' % self.counter | |||
if self.column + 1 + len(text) >= WIDTH: | if self.column + 1 + len(text) >= WIDTH: | |||
write(u'\n%5d ' % self.counter) | write(u'\n%5d ' % self.counter) | |||
else: | else: | |||
text = u' ' + text | text = u' ' + text | |||
write(text + u'\n') | write(text + u'\n') | |||
except Exit, exception: | except Exit as exception: | |||
if not self.verbose: | if not self.verbose: | |||
write(u'\n') | write(u'\n') | |||
write(u'\n* %s *\n' % str(exception)) | write(u'\n* %s *\n' % str(exception)) | |||
except Limit_Reached: | except Limit_Reached: | |||
if not self.verbose: | if not self.verbose: | |||
write(u'\n') | write(u'\n') | |||
if not self.save: | if not self.save: | |||
if len(self.failures) == 1: | if len(self.failures) == 1: | |||
write(u'\n* One error already! *\n') | write(u'\n* One error already! *\n') | |||
else: | else: | |||
skipping to change at line 217 | skipping to change at line 217 | |||
if self.skip_count == 1: | if self.skip_count == 1: | |||
text += u", one skipped" | text += u", one skipped" | |||
else: | else: | |||
text += u", %d skipped" % self.skip_count | text += u", %d skipped" % self.skip_count | |||
if first: | if first: | |||
text = u"No test" | text = u"No test" | |||
summary = (u"\nSummary: %s in %.2f seconds.\n" | summary = (u"\nSummary: %s in %.2f seconds.\n" | |||
% (text, time.time() - start_time)) | % (text, time.time() - start_time)) | |||
write(summary) | write(summary) | |||
if self.save: | if self.save: | |||
write = file(self.save, u'w').write | write = open(self.save, u'w').write | |||
for ordinal in self.failures: | for ordinal in self.failures: | |||
write(u'%d\n' % ordinal) | write(u'%d\n' % ordinal) | |||
else: | else: | |||
write = sys.stdout.write | write = sys.stdout.write | |||
for (ordinal, prefix, function, arguments, stdout, stderr, | for (ordinal, prefix, function, arguments, stdout, stderr, | |||
tracing) in self.failures: | tracing) in self.failures: | |||
write(u'\n' + u'=' * WIDTH + u'\n') | write(u'\n' + u'=' * WIDTH + u'\n') | |||
write(u'%d. %s\n' % (ordinal, prefix)) | write(u'%d. %s\n' % (ordinal, prefix)) | |||
if function and function.__name__ != os.path.basename(prefix): | if function and function.__name__ != os.path.basename(prefix): | |||
write(u" Function %s\n" % function.__name__) | write(u" Function %s\n" % function.__name__) | |||
skipping to change at line 266 | skipping to change at line 266 | |||
sys.exit("%s: Should have a `.py' suffix." % path) | sys.exit("%s: Should have a `.py' suffix." % path) | |||
yield path | yield path | |||
def handle_module(self, prefix, module): | def handle_module(self, prefix, module): | |||
collection = [] | collection = [] | |||
for name, objet in inspect.getmembers(module): | for name, objet in inspect.getmembers(module): | |||
if name.startswith(u'Test') and inspect.isclass(objet): | if name.startswith(u'Test') and inspect.isclass(objet): | |||
if getattr(object, u'disabled', False): | if getattr(object, u'disabled', False): | |||
continue | continue | |||
minimum = None | minimum = None | |||
for _, method in inspect.getmembers(objet, inspect.ismethod): | for _, func in inspect.getmembers(objet, inspect.isfunction): | |||
number = method.im_func.func_code.co_firstlineno | number = func.__code__.co_firstlineno | |||
if minimum is None or number < minimum: | if minimum is None or number < minimum: | |||
minimum = number | minimum = number | |||
if minimum is not None: | if minimum is not None: | |||
collection.append((minimum, name, objet, False)) | collection.append((minimum, name, objet, False)) | |||
elif name.startswith(u'test_') and inspect.isfunction(objet): | elif name.startswith(u'test_') and inspect.isfunction(objet): | |||
code = objet.func_code | code = objet.__code__ | |||
collection.append((code.co_firstlineno, name, objet, | collection.append((code.co_firstlineno, name, objet, | |||
bool(code.co_flags & 32))) | bool(code.co_flags & 32))) | |||
if not collection: | if not collection: | |||
return | return | |||
self.delayed_setup_module = None | self.delayed_setup_module = None | |||
self.did_tests_in_module = False | self.did_tests_in_module = False | |||
if hasattr(module, u'setup_module'): | if hasattr(module, u'setup_module'): | |||
self.delayed_setup_module = module.setup_module, module | self.delayed_setup_module = module.setup_module, module | |||
for _, name, objet, generator in sorted(collection): | for _, name, objet, generator in sorted(collection): | |||
self.delayed_setup_class = None | self.delayed_setup_class = None | |||
skipping to change at line 296 | skipping to change at line 296 | |||
if not getattr(object, u'disabled', False): | if not getattr(object, u'disabled', False): | |||
self.handle_class(prefix + u'/' + name, objet) | self.handle_class(prefix + u'/' + name, objet) | |||
else: | else: | |||
self.handle_function(prefix + u'/' + name, objet, | self.handle_function(prefix + u'/' + name, objet, | |||
generator, None) | generator, None) | |||
if self.did_tests_in_module and hasattr(module, u'teardown_module'): | if self.did_tests_in_module and hasattr(module, u'teardown_module'): | |||
module.teardown_module(module) | module.teardown_module(module) | |||
def handle_class(self, prefix, classe): | def handle_class(self, prefix, classe): | |||
collection = [] | collection = [] | |||
for name, method in inspect.getmembers(classe, inspect.ismethod): | for name, func in inspect.getmembers(classe, inspect.isfunction): | |||
if name.startswith(u'test_'): | if name.startswith(u'test_'): | |||
code = method.im_func.func_code | code = func.__code__ | |||
collection.append((code.co_firstlineno, name, method, | collection.append((code.co_firstlineno, name, func, | |||
bool(code.co_flags & 32))) | bool(code.co_flags & inspect.CO_GENERATOR))) | |||
if not collection: | if not collection: | |||
return | return | |||
# FIXME: Should likely do module setup here! | # FIXME: Should likely do module setup here! | |||
instance = classe() | instance = classe() | |||
if hasattr(instance, u'setup_class'): | if hasattr(instance, u'setup_class'): | |||
self.delayed_setup_module = instance.setup_class, classe | self.delayed_setup_module = instance.setup_class, classe | |||
for _, name, method, generator in sorted(collection): | for _, name, method, generator in sorted(collection): | |||
self.handle_function(prefix + u'/' + name, getattr(instance, name), | self.handle_function(prefix + u'/' + name, getattr(instance, name), | |||
generator, instance) | generator, instance) | |||
if self.did_tests_in_class and hasattr(instance, u'teardown_class'): | if self.did_tests_in_class and hasattr(instance, u'teardown_class'): | |||
instance.teardown_class(classe) | instance.teardown_class(classe) | |||
def handle_function(self, prefix, function, generator, instance): | def handle_function(self, prefix, function, generator, instance): | |||
collection = [] | collection = [] | |||
if generator: | if generator: | |||
# FIXME: Should likely do class setup here. | # FIXME: Should likely do class setup here. | |||
try: | try: | |||
for counter, arguments in enumerate(function()): | for counter, arguments in enumerate(function()): | |||
collection.append((prefix + u'/' + unicode(counter + 1), | collection.append((prefix + u'/' + str(counter + 1), | |||
arguments[0], arguments[1:])) | arguments[0], arguments[1:])) | |||
except Skipped: | except Skipped: | |||
return | return | |||
else: | else: | |||
collection.append((prefix, function, ())) | collection.append((prefix, function, ())) | |||
for prefix, function, arguments in collection: | for prefix, function, arguments in collection: | |||
self.launch_test(prefix, function, arguments, instance) | self.launch_test(prefix, function, arguments, instance) | |||
def launch_test(self, prefix, function, arguments, instance): | def launch_test(self, prefix, function, arguments, instance): | |||
# Check if this test should be retained. | # Check if this test should be retained. | |||
skipping to change at line 386 | skipping to change at line 386 | |||
stderr = None | stderr = None | |||
self.mark_progression(prefix, success) | self.mark_progression(prefix, success) | |||
if success is False: | if success is False: | |||
if self.save: | if self.save: | |||
self.failures.append(self.total_count) | self.failures.append(self.total_count) | |||
else: | else: | |||
tracing = StringIO() | tracing = StringIO() | |||
traceback.print_exc(file=tracing) | traceback.print_exc(file=tracing) | |||
self.failures.append( | self.failures.append( | |||
(self.total_count, prefix, function, arguments, | (self.total_count, prefix, function, arguments, | |||
stdout, stderr, str(tracing.getvalue()))) | stdout, stderr, tracing.getvalue())) | |||
if instance is not None and hasattr(instance, u'teardown_method'): | if instance is not None and hasattr(instance, u'teardown_method'): | |||
instance.teardown_method(function) | instance.teardown_method(function) | |||
self.did_tests_in_class = True | self.did_tests_in_class = True | |||
self.did_tests_in_module = True | self.did_tests_in_module = True | |||
if success is not None and len(self.failures) == self.limit: | if success is not None and len(self.failures) == self.limit: | |||
raise Limit_Reached | raise Limit_Reached | |||
def mark_progression(self, prefix, success): | def mark_progression(self, prefix, success): | |||
self.total_count += 1 | self.total_count += 1 | |||
if self.verbose: | if self.verbose: | |||
skipping to change at line 451 | skipping to change at line 451 | |||
def __init__(self, stream): | def __init__(self, stream): | |||
import codecs, locale | import codecs, locale | |||
writer = codecs.getwriter(locale.getpreferredencoding()) | writer = codecs.getwriter(locale.getpreferredencoding()) | |||
self.stream = writer(stream, 'backslashreplace') | self.stream = writer(stream, 'backslashreplace') | |||
def flush(self): | def flush(self): | |||
self.stream.flush() | self.stream.flush() | |||
def write(self, text): | def write(self, text): | |||
if not isinstance(text, unicode): | if not isinstance(text, unicode): | |||
text = unicode(text, 'UTF-8') | text = text.encode('UTF-8') | |||
self.stream.write(text) | self.stream.write(text) | |||
def writelines(self, lines): | def writelines(self, lines): | |||
for line in lines: | for line in lines: | |||
self.write(line) | self.write(line) | |||
run = Main() | run = Main() | |||
main = run.main | main = run.main | |||
class Exit(Exception): pass | class Exit(Exception): pass | |||
End of changes. 12 change blocks. | ||||
15 lines changed or deleted | 15 lines changed or added |