summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--CHANGELOG55
-rw-r--r--doc/writing_tests.rst4
-rw-r--r--functional_tests/support/issue720/test.py6
-rw-r--r--functional_tests/test_failuredetail_plugin.py14
-rw-r--r--nose/config.py3
-rw-r--r--nose/failure.py3
-rw-r--r--nose/plugins/capture.py27
-rw-r--r--nose/plugins/failuredetail.py8
-rw-r--r--nose/plugins/logcapture.py3
-rw-r--r--nose/plugins/skip.py19
-rw-r--r--nose/plugins/xunit.py55
-rw-r--r--nose/pyversion.py65
-rw-r--r--nose/suite.py1
-rw-r--r--nose/usage.txt5
-rw-r--r--nose/util.py9
-rw-r--r--unit_tests/test_loader.py46
-rw-r--r--unit_tests/test_logcapture_plugin.py20
-rw-r--r--unit_tests/test_xunit.py34
19 files changed, 266 insertions, 112 deletions
diff --git a/AUTHORS b/AUTHORS
index dce5b1c..5414bcd 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -24,3 +24,4 @@ Gary Donovan
Brendan McCollam
Erik Rose
Sascha Peilicke
+Andre Caron
diff --git a/CHANGELOG b/CHANGELOG
index f024439..ec09ab9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,9 +1,64 @@
In Development
+- The log capture plugin now correctly applies filters that were added
+ using `addFilter`.
+ Patch by Malthe Borch.
- Corrected a reference to the multiprocessing plugin in the documentation.
Patch by Nick Loadholtes.
- Fixed #447: doctests fail when getpackage() returns None
Patch by Matthew Brett.
+- Fixed #749: xunit exceeds recursion limit
+ Patch by André Caron.
+- Fixed a number of unicode-related issues.
+ Patch by John Szakmeister.
+- Added the ability to ignore config files via an environment variable
+ Patch by Lukasz Balcerzak
+- Fixed #720: nose with detailed errors raises encoding error
+ Patch by John Szakmeister. Thanks to Guillaume Ayoub for the test case.
+- Fixed #692: UnicodeDecodeError in xunit when capturing stdout and stderr
+ Patch by John Szakmeister.
+- Fixed #693: Python 2.4 incompatibilities
+ Patch by John Szakmeister.
+- Don't save zero-byte xunit test reports
+ Patch by Dan Savilonis.
+- Fix Importer.importFromPath to be able to import modules whose names start
+ with __init__
+ Patch by Paul Bonser.
+- Add a fake isatty() method to Tee
+ Patch by Jimmy Wennlund.
+- Fix #700: Tee is missing the writelines() method
+ Patch by John Szakmeister.
+- Fix #649: UnicodeDecodeError when an exception contains encoded strings
+ Patch by John Szakmeister.
+- Fix #687: verbosity is not a flag
+ Patch by John Szakmeister.
+- Fixed a suppressed deprecation warning
+ Patch by Arnon Yaari.
+- Fixed some broken links in the documentation
+ Patch by Arnon Yaari.
+- Add missing format parameter in error message
+ Patch by Etienne Millon.
+- Switched docs to point at the GitHub site for the issue tracker
+ Patch by Daniel Beck.
+- Fix #447: doctests fail when getpackage() returns None
+ Patch by Matthew Brett.
+- Fix #366: make --pdb report on errors and failures. Use --pdb-error to get
+ the old behavior.
+ Patch by Arnon Yaari.
+- Fix #501: Imported test generators are misrecognized as simple test
+ functions
+ Patch by John Szakmeister.
+- Added a test for issue #501
+ Patch by Michael Killough.
+- Use SkipTest from unittest2, if available, for better integration with
+ testtools
+ Patch by Ian Wienand.
+- Fix #759: Test failures with Python 3.4
+ Patch by Barry Warsaw.
+- Add a note about executable files in the usage, and how to workaround it
+ Patch by Michael Dunn.
+- Fix #743: fix an incorrect regex in writing_tests.rst
+ Patch by Anne Moroney.
1.3.0
diff --git a/doc/writing_tests.rst b/doc/writing_tests.rst
index d2418bd..4ccb9f4 100644
--- a/doc/writing_tests.rst
+++ b/doc/writing_tests.rst
@@ -3,7 +3,7 @@ Writing tests
As with py.test_, nose tests need not be subclasses of
:class:`unittest.TestCase`. Any function or class that matches the configured
-testMatch regular expression (``(?:^|[\\b_\\.-])[Tt]est)`` by default -- that
+testMatch regular expression (``(?:^|[\\b_\\.-])[Tt]est`` by default -- that
is, has test or Test at a word boundary or following a - or _) and lives in a
module that also matches that expression will be run as a test. For the sake
of compatibility with legacy unittest test cases, nose will also load tests
@@ -169,4 +169,4 @@ methods *do not* run before the generator method itself, as this would cause
setUp to run twice before the first test without an intervening tearDown.
Please note that method generators *are not* supported in `unittest.TestCase`
-subclasses. \ No newline at end of file
+subclasses.
diff --git a/functional_tests/support/issue720/test.py b/functional_tests/support/issue720/test.py
new file mode 100644
index 0000000..0a194fd
--- /dev/null
+++ b/functional_tests/support/issue720/test.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+import unittest
+class Test(unittest.TestCase):
+ def test(self):
+ print u"Unicöde"
+ assert 1 == 2
diff --git a/functional_tests/test_failuredetail_plugin.py b/functional_tests/test_failuredetail_plugin.py
index 284cf49..8484461 100644
--- a/functional_tests/test_failuredetail_plugin.py
+++ b/functional_tests/test_failuredetail_plugin.py
@@ -46,5 +46,19 @@ class TestFailureDetailWithCapture(PluginTester, unittest.TestCase):
assert expect in self.output
+class TestFailureDetailWithUnicodeAndCapture(PluginTester, unittest.TestCase):
+ activate = "-d"
+ args = ['-v']
+ plugins = [FailureDetail(), Capture()]
+ suitepath = os.path.join(support, 'issue720')
+
+ def runTest(self):
+ print '*' * 70
+ print str(self.output)
+ print '*' * 70
+
+ assert 'UnicodeDecodeError' not in self.output
+ assert 'UnicodeEncodeError' not in self.output
+
if __name__ == '__main__':
unittest.main()
diff --git a/nose/config.py b/nose/config.py
index 979fe3b..4214c2d 100644
--- a/nose/config.py
+++ b/nose/config.py
@@ -623,9 +623,6 @@ class NoOptions(object):
def __getnewargs__(self):
return ()
- def __getattr__(self, attr):
- return None
-
def __nonzero__(self):
return False
diff --git a/nose/failure.py b/nose/failure.py
index d24401c..c5fabfd 100644
--- a/nose/failure.py
+++ b/nose/failure.py
@@ -1,6 +1,7 @@
import logging
import unittest
from traceback import format_tb
+from nose.pyversion import is_base_exception
log = logging.getLogger(__name__)
@@ -34,7 +35,7 @@ class Failure(unittest.TestCase):
def runTest(self):
if self.tb is not None:
- if isinstance(self.exc_val, BaseException):
+ if is_base_exception(self.exc_val):
raise self.exc_val, None, self.tb
raise self.exc_class, self.exc_val, self.tb
else:
diff --git a/nose/plugins/capture.py b/nose/plugins/capture.py
index 224f0a5..fa4e5dc 100644
--- a/nose/plugins/capture.py
+++ b/nose/plugins/capture.py
@@ -13,6 +13,7 @@ import logging
import os
import sys
from nose.plugins.base import Plugin
+from nose.pyversion import exc_to_unicode, force_unicode
from nose.util import ln
from StringIO import StringIO
@@ -86,30 +87,8 @@ class Capture(Plugin):
return self.formatError(test, err)
def addCaptureToErr(self, ev, output):
- if isinstance(ev, BaseException):
- if hasattr(ev, '__unicode__'):
- # 2.6+
- try:
- ev = unicode(ev)
- except UnicodeDecodeError:
- # We need a unicode string... take our best shot at getting,
- # since we don't know what the original encoding is in.
- ev = str(ev).decode('utf8', 'replace')
- else:
- # 2.5-
- if not hasattr(ev, 'message'):
- # 2.4
- msg = len(ev.args) and ev.args[0] or ''
- else:
- msg = ev.message
- if (isinstance(msg, basestring) and
- not isinstance(msg, unicode)):
- msg = msg.decode('utf8', 'replace')
- ev = u'%s: %s' % (ev.__class__.__name__, msg)
- elif not isinstance(ev, basestring):
- ev = repr(ev)
- if not isinstance(output, unicode):
- output = output.decode('utf8', 'replace')
+ ev = exc_to_unicode(ev)
+ output = force_unicode(output)
return u'\n'.join([ev, ln(u'>> begin captured stdout <<'),
output, ln(u'>> end captured stdout <<')])
diff --git a/nose/plugins/failuredetail.py b/nose/plugins/failuredetail.py
index 4c0729c..6462865 100644
--- a/nose/plugins/failuredetail.py
+++ b/nose/plugins/failuredetail.py
@@ -7,6 +7,7 @@ debugging information.
"""
from nose.plugins import Plugin
+from nose.pyversion import exc_to_unicode, force_unicode
from nose.inspector import inspect_traceback
class FailureDetail(Plugin):
@@ -38,10 +39,11 @@ class FailureDetail(Plugin):
"""Add detail from traceback inspection to error message of a failure.
"""
ec, ev, tb = err
- tbinfo, str_ev = None, str(ev)
+ tbinfo, str_ev = None, exc_to_unicode(ev)
+
if tb:
- tbinfo = inspect_traceback(tb)
- str_ev = '\n'.join([str(ev), tbinfo])
+ tbinfo = force_unicode(inspect_traceback(tb))
+ str_ev = '\n'.join([str_ev, tbinfo])
test.tbinfo = tbinfo
return (ec, str_ev, tb)
diff --git a/nose/plugins/logcapture.py b/nose/plugins/logcapture.py
index 30b4a96..101b335 100644
--- a/nose/plugins/logcapture.py
+++ b/nose/plugins/logcapture.py
@@ -85,7 +85,8 @@ class MyMemoryHandler(Handler):
def truncate(self):
self.buffer = []
def filter(self, record):
- return self.filterset.allow(record.name)
+ if self.filterset.allow(record.name):
+ return Handler.filter(self, record)
def __getstate__(self):
state = self.__dict__.copy()
del state['lock']
diff --git a/nose/plugins/skip.py b/nose/plugins/skip.py
index 27e5162..9d1ac8f 100644
--- a/nose/plugins/skip.py
+++ b/nose/plugins/skip.py
@@ -9,15 +9,22 @@ is enabled by default but may be disabled with the ``--no-skip`` option.
from nose.plugins.errorclass import ErrorClass, ErrorClassPlugin
+# on SkipTest:
+# - unittest SkipTest is first preference, but it's only available
+# for >= 2.7
+# - unittest2 SkipTest is second preference for older pythons. This
+# mirrors logic for choosing SkipTest exception in testtools
+# - if none of the above, provide custom class
try:
- # 2.7
from unittest.case import SkipTest
except ImportError:
- # 2.6 and below
- class SkipTest(Exception):
- """Raise this exception to mark a test as skipped.
- """
- pass
+ try:
+ from unittest2.case import SkipTest
+ except ImportError:
+ class SkipTest(Exception):
+ """Raise this exception to mark a test as skipped.
+ """
+ pass
class Skip(ErrorClassPlugin):
diff --git a/nose/plugins/xunit.py b/nose/plugins/xunit.py
index 88255b9..d0aed88 100644
--- a/nose/plugins/xunit.py
+++ b/nose/plugins/xunit.py
@@ -49,7 +49,7 @@ from xml.sax import saxutils
from nose.plugins.base import Plugin
from nose.exc import SkipTest
-from nose.pyversion import UNICODE_STRINGS
+from nose.pyversion import force_unicode, format_exception
# Invalid XML characters, control characters 0-31 sans \t, \n and \r
CONTROL_CHARACTERS = re.compile(r"[\000-\010\013\014\016-\037]")
@@ -112,26 +112,16 @@ def exc_message(exc_info):
# Fallback to args as neither str nor
# unicode(Exception(u'\xe6')) work in Python < 2.6
result = exc.args[0]
+ result = force_unicode(result, 'UTF-8')
return xml_safe(result)
-def format_exception(exc_info):
- ec, ev, tb = exc_info
-
- # formatError() may have turned our exception object into a string, and
- # Python 3's traceback.format_exception() doesn't take kindly to that (it
- # expects an actual exception object). So we work around it, by doing the
- # work ourselves if ev is a string.
- if isinstance(ev, basestring):
- tb_data = ''.join(traceback.format_tb(tb))
- return tb_data + ev
- else:
- return ''.join(traceback.format_exception(*exc_info))
-
class Tee(object):
- def __init__(self, *args):
+ def __init__(self, encoding, *args):
+ self._encoding = encoding
self._streams = args
def write(self, data):
+ data = force_unicode(data, self._encoding)
for s in self._streams:
s.write(data)
@@ -173,8 +163,6 @@ class Xunit(Plugin):
def _quoteattr(self, attr):
"""Escape an XML attribute. Value can be unicode."""
attr = xml_safe(attr)
- if isinstance(attr, unicode) and not UNICODE_STRINGS:
- attr = attr.encode(self.encoding)
return saxutils.quoteattr(attr)
def options(self, parser, env):
@@ -217,7 +205,7 @@ class Xunit(Plugin):
u'<testsuite name="nosetests" tests="%(total)d" '
u'errors="%(errors)d" failures="%(failures)d" '
u'skip="%(skipped)d">' % self.stats)
- self.error_report_file.write(u''.join([self._forceUnicode(e)
+ self.error_report_file.write(u''.join([force_unicode(e, self.encoding)
for e in self.errorlist]))
self.error_report_file.write(u'</testsuite>')
self.error_report_file.close()
@@ -229,12 +217,15 @@ class Xunit(Plugin):
self._capture_stack.append((sys.stdout, sys.stderr))
self._currentStdout = StringIO()
self._currentStderr = StringIO()
- sys.stdout = Tee(self._currentStdout, sys.stdout)
- sys.stderr = Tee(self._currentStderr, sys.stderr)
+ sys.stdout = Tee(self.encoding, self._currentStdout, sys.stdout)
+ sys.stderr = Tee(self.encoding, self._currentStderr, sys.stderr)
def startContext(self, context):
self._startCapture()
+ def stopContext(self, context):
+ self._endCapture()
+
def beforeTest(self, test):
"""Initializes a timer before starting a test."""
self._timer = time()
@@ -281,12 +272,13 @@ class Xunit(Plugin):
type = 'error'
self.stats['errors'] += 1
- tb = format_exception(err)
+ tb = format_exception(err, self.encoding)
id = test.id()
+
self.errorlist.append(
- '<testcase classname=%(cls)s name=%(name)s time="%(taken).3f">'
- '<%(type)s type=%(errtype)s message=%(message)s><![CDATA[%(tb)s]]>'
- '</%(type)s>%(systemout)s%(systemerr)s</testcase>' %
+ u'<testcase classname=%(cls)s name=%(name)s time="%(taken).3f">'
+ u'<%(type)s type=%(errtype)s message=%(message)s><![CDATA[%(tb)s]]>'
+ u'</%(type)s>%(systemout)s%(systemerr)s</testcase>' %
{'cls': self._quoteattr(id_split(id)[0]),
'name': self._quoteattr(id_split(id)[-1]),
'taken': taken,
@@ -302,13 +294,14 @@ class Xunit(Plugin):
"""Add failure output to Xunit report.
"""
taken = self._timeTaken()
- tb = format_exception(err)
+ tb = format_exception(err, self.encoding)
self.stats['failures'] += 1
id = test.id()
+
self.errorlist.append(
- '<testcase classname=%(cls)s name=%(name)s time="%(taken).3f">'
- '<failure type=%(errtype)s message=%(message)s><![CDATA[%(tb)s]]>'
- '</failure>%(systemout)s%(systemerr)s</testcase>' %
+ u'<testcase classname=%(cls)s name=%(name)s time="%(taken).3f">'
+ u'<failure type=%(errtype)s message=%(message)s><![CDATA[%(tb)s]]>'
+ u'</failure>%(systemout)s%(systemerr)s</testcase>' %
{'cls': self._quoteattr(id_split(id)[0]),
'name': self._quoteattr(id_split(id)[-1]),
'taken': taken,
@@ -334,9 +327,3 @@ class Xunit(Plugin):
'systemout': self._getCapturedStdout(),
'systemerr': self._getCapturedStderr(),
})
-
- def _forceUnicode(self, s):
- if not UNICODE_STRINGS:
- if isinstance(s, str):
- s = s.decode(self.encoding, 'replace')
- return s
diff --git a/nose/pyversion.py b/nose/pyversion.py
index a6ec3f7..07c105f 100644
--- a/nose/pyversion.py
+++ b/nose/pyversion.py
@@ -3,19 +3,33 @@ This module contains fixups for using nose under different versions of Python.
"""
import sys
import os
+import traceback
import types
import inspect
import nose.util
__all__ = ['make_instancemethod', 'cmp_to_key', 'sort_list', 'ClassType',
'TypeType', 'UNICODE_STRINGS', 'unbound_method', 'ismethod',
- 'bytes_']
+ 'bytes_', 'is_base_exception', 'force_unicode', 'exc_to_unicode',
+ 'format_exception']
# In Python 3.x, all strings are unicode (the call to 'unicode()' in the 2.x
# source will be replaced with 'str()' when running 2to3, so this test will
# then become true)
UNICODE_STRINGS = (type(unicode()) == type(str()))
+if sys.version_info[:2] < (3, 0):
+ def force_unicode(s, encoding='UTF-8'):
+ try:
+ s = unicode(s)
+ except UnicodeDecodeError:
+ s = str(s).decode(encoding, 'replace')
+
+ return s
+else:
+ def force_unicode(s, encoding='UTF-8'):
+ return str(s)
+
# new.instancemethod() is obsolete for new-style classes (Python 3.x)
# We need to use descriptor methods instead.
try:
@@ -147,3 +161,52 @@ else:
return func.func_code.co_flags & CO_GENERATOR != 0
except AttributeError:
return False
+
+# Make a function to help check if an exception is derived from BaseException.
+# In Python 2.4, we just use Exception instead.
+if sys.version_info[:2] < (2, 5):
+ def is_base_exception(exc):
+ return isinstance(exc, Exception)
+else:
+ def is_base_exception(exc):
+ return isinstance(exc, BaseException)
+
+if sys.version_info[:2] < (3, 0):
+ def exc_to_unicode(ev, encoding='utf-8'):
+ if is_base_exception(ev):
+ if not hasattr(ev, '__unicode__'):
+ # 2.5-
+ if not hasattr(ev, 'message'):
+ # 2.4
+ msg = len(ev.args) and ev.args[0] or ''
+ else:
+ msg = ev.message
+ msg = force_unicode(msg, encoding=encoding)
+ clsname = force_unicode(ev.__class__.__name__,
+ encoding=encoding)
+ ev = u'%s: %s' % (clsname, msg)
+ elif not isinstance(ev, unicode):
+ ev = repr(ev)
+
+ return force_unicode(ev, encoding=encoding)
+else:
+ def exc_to_unicode(ev, encoding='utf-8'):
+ return str(ev)
+
+def format_exception(exc_info, encoding='UTF-8'):
+ ec, ev, tb = exc_info
+
+ # Our exception object may have been turned into a string, and Python 3's
+ # traceback.format_exception() doesn't take kindly to that (it expects an
+ # actual exception object). So we work around it, by doing the work
+ # ourselves if ev is not an exception object.
+ if not is_base_exception(ev):
+ tb_data = force_unicode(
+ ''.join(traceback.format_tb(tb)),
+ encoding)
+ ev = exc_to_unicode(ev)
+ return tb_data + ev
+ else:
+ return force_unicode(
+ ''.join(traceback.format_exception(*exc_info)),
+ encoding)
diff --git a/nose/suite.py b/nose/suite.py
index 3b68b23..18098ca 100644
--- a/nose/suite.py
+++ b/nose/suite.py
@@ -573,6 +573,7 @@ class FinalizingSuiteWrapper(unittest.TestSuite):
control.
"""
def __init__(self, suite, finalize):
+ super(FinalizingSuiteWrapper, self).__init__()
self.suite = suite
self.finalize = finalize
diff --git a/nose/usage.txt b/nose/usage.txt
index 21ef5ce..bc96894 100644
--- a/nose/usage.txt
+++ b/nose/usage.txt
@@ -17,6 +17,11 @@ may use the assert keyword or raise AssertionErrors to indicate test
failure. TestCase subclasses may do the same or use the various
TestCase methods available.
+**It is important to note that the default behavior of nose is to
+not include tests from files which are executable.** To include
+tests from such files, remove their executable bit or use
+the --exe flag (see 'Options' section below).
+
Selecting Tests
---------------
diff --git a/nose/util.py b/nose/util.py
index 039163a..7995700 100644
--- a/nose/util.py
+++ b/nose/util.py
@@ -609,8 +609,13 @@ def transplant_func(func, module):
"""
from nose.tools import make_decorator
- def newfunc(*arg, **kw):
- return func(*arg, **kw)
+ if isgenerator(func):
+ def newfunc(*arg, **kw):
+ for v in func(*arg, **kw):
+ yield v
+ else:
+ def newfunc(*arg, **kw):
+ return func(*arg, **kw)
newfunc = make_decorator(func)(newfunc)
newfunc.__module__ = module
diff --git a/unit_tests/test_loader.py b/unit_tests/test_loader.py
index 1f622a5..e2dfcc4 100644
--- a/unit_tests/test_loader.py
+++ b/unit_tests/test_loader.py
@@ -35,6 +35,8 @@ def mods():
M['test_module_with_metaclass_tests'] = imp.new_module(
'test_module_with_metaclass_tests')
M['test_transplant'] = imp.new_module('test_transplant')
+ M['test_module_transplant_generator'] = imp.new_module(
+ 'test_module_transplant_generator')
# a unittest testcase subclass
class TC(unittest.TestCase):
@@ -112,6 +114,14 @@ def mods():
def runTest(self):
pass
+ def test_func_generator_transplant():
+ """docstring for transplanted test func generator
+ """
+ def test_odd(v):
+ assert v % 2
+ for i in range(0, 4):
+ yield test_odd, i
+
M['nose'] = nose
M['__main__'] = sys.modules['__main__']
M['test_module'].TC = TC
@@ -133,6 +143,9 @@ def mods():
TestMetaclassed.__module__ = 'test_module_with_metaclass_tests'
M['test_transplant'].Transplant = Transplant
Transplant.__module__ = 'test_class_source'
+ M['test_module_transplant_generator'].test_func_generator_transplant = \
+ test_func_generator_transplant
+ # don't set test_func_generator_transplant.__module__ so it is transplanted
del TC
del TC2
del TestMetaclassed
@@ -141,6 +154,7 @@ def mods():
del TestClass
del test_func_generator
del Transplant
+ del test_func_generator_transplant
return M
M = mods()
@@ -514,7 +528,37 @@ class TestTestLoader(unittest.TestCase):
count += 1
assert count == 4, \
"Expected to generate 4 tests, but got %s" % count
-
+
+ def test_load_transplanted_generator(self):
+ print "load transplanted generator (issue 501)"
+ test_module_transplant_generator = M['test_module_transplant_generator']
+ l = self.l
+ suite = l.loadTestsFromModule(test_module_transplant_generator)
+ tests = [t for t in suite]
+
+ assert len(tests) == 1
+ print "test", tests[0]
+ assert isinstance(tests[0], unittest.TestSuite), \
+ "Test is not a suite - probably did not look like a generator"
+
+ count = 0
+ for t in tests[0]:
+ print "generated test %s" % t
+ print t.shortDescription()
+ assert isinstance(t, nose.case.Test), \
+ "Test %s is not a Test?" % t
+ assert isinstance(t.test, nose.case.FunctionTestCase), \
+ "Test %s is not a FunctionTestCase" % t.test
+ assert 'test_func_generator' in str(t), \
+ "Bad str val '%s' for test" % str(t)
+ assert 'docstring for transplanted test func generator' \
+ in t.shortDescription(), \
+ "Bad shortDescription '%s' for test %s" % \
+ (t.shortDescription(), t)
+ count += 1
+ assert count == 4, \
+ "Expected to generate 4 tests, but got %s" % count
+
if __name__ == '__main__':
#import logging
#logging.basicConfig(level=logging.DEBUG)
diff --git a/unit_tests/test_logcapture_plugin.py b/unit_tests/test_logcapture_plugin.py
index 63d1f8b..63aa651 100644
--- a/unit_tests/test_logcapture_plugin.py
+++ b/unit_tests/test_logcapture_plugin.py
@@ -155,6 +155,26 @@ class TestLogCapturePlugin(object):
eq_(1, len(records))
eq_("++Hello++", records[0])
+ def test_builtin_logging_filtering(self):
+ c = LogCapture()
+ c.logformat = '++%(message)s++'
+ c.start()
+ log = logging.getLogger("foobar.something")
+ filtered = []
+ class filter(object):
+ def filter(record):
+ filtered.append(record)
+ return len(filtered) == 1
+ filter = staticmethod(filter)
+ c.handler.addFilter(filter)
+ log.debug("Hello")
+ log.debug("World")
+ c.end()
+ eq_(2, len(filtered))
+ records = c.formatLogRecords()
+ eq_(1, len(records))
+ eq_("++Hello++", records[0])
+
def test_logging_filter(self):
env = {'NOSE_LOGFILTER': 'foo,bar'}
c = LogCapture()
diff --git a/unit_tests/test_xunit.py b/unit_tests/test_xunit.py
index c141739..d98ccba 100644
--- a/unit_tests/test_xunit.py
+++ b/unit_tests/test_xunit.py
@@ -23,40 +23,6 @@ mktest.__test__ = False
time_taken = re.compile(r'\d\.\d\d')
-class TestEscaping(unittest.TestCase):
-
- def setUp(self):
- self.x = Xunit()
-
- def test_all(self):
- eq_(self.x._quoteattr(
- '''<baz src="http://foo?f=1&b=2" quote="inix hubris 'maximus'?" />'''),
- ('"&lt;baz src=&quot;http://foo?f=1&amp;b=2&quot; '
- 'quote=&quot;inix hubris \'maximus\'?&quot; /&gt;"'))
-
- def test_unicode_is_utf8_by_default(self):
- if not UNICODE_STRINGS:
- eq_(self.x._quoteattr(u'Ivan Krsti\u0107'),
- '"Ivan Krsti\xc4\x87"')
-
- def test_unicode_custom_utf16_madness(self):
- self.x.encoding = 'utf-16'
- utf16 = self.x._quoteattr(u'Ivan Krsti\u0107')[1:-1]
-
- if UNICODE_STRINGS:
- # If all internal strings are unicode, then _quoteattr shouldn't
- # have changed anything.
- eq_(utf16, u'Ivan Krsti\u0107')
- else:
- # to avoid big/little endian bytes, assert that we can put it back:
- eq_(utf16.decode('utf16'), u'Ivan Krsti\u0107')
-
- def test_control_characters(self):
- # quoting of \n, \r varies in diff. python versions
- n = saxutils.quoteattr('\n')[1:-1]
- r = saxutils.quoteattr('\r')[1:-1]
- eq_(self.x._quoteattr('foo\n\b\f\r'), '"foo%s??%s"' % (n, r))
- eq_(escape_cdata('foo\n\b\f\r'), 'foo\n??\r')
class TestSplitId(unittest.TestCase):