Rev 4802: Move the passing of test logs to the result to be via the getDetails API and remove all public use of TestCase._get_log. in http://bazaar.launchpad.net/~lifeless/bzr/subunit

Robert Collins robertc at robertcollins.net
Sun Dec 6 00:13:00 GMT 2009


At http://bazaar.launchpad.net/~lifeless/bzr/subunit

------------------------------------------------------------
revno: 4802
revision-id: robertc at robertcollins.net-20091206001245-x1nv37nkl21xktyy
parent: robertc at robertcollins.net-20091205100734-xobmvk2j4yk8kuvw
committer: Robert Collins <robertc at robertcollins.net>
branch nick: subunit
timestamp: Sun 2009-12-06 11:12:45 +1100
message:
  Move the passing of test logs to the result to be via the getDetails API and remove all public use of TestCase._get_log.
=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2009-12-05 10:07:34 +0000
+++ b/bzrlib/tests/__init__.py	2009-12-06 00:12:45 +0000
@@ -46,6 +46,7 @@
 import tempfile
 import threading
 import time
+import traceback
 import unittest
 import warnings
 
@@ -364,26 +365,6 @@
         self.not_applicable_count += 1
         self.report_not_applicable(test, reason)
 
-    def printErrorList(self, flavour, errors):
-        for test, err in errors:
-            self.stream.writeln(self.separator1)
-            self.stream.write("%s: " % flavour)
-            self.stream.writeln(self.getDescription(test))
-            if getattr(test, '_get_log', None) is not None:
-                log_contents = test._get_log()
-                if log_contents:
-                    self.stream.write('\n')
-                    self.stream.write(
-                            ('vvvv[log from %s]' % test.id()).ljust(78,'-'))
-                    self.stream.write('\n')
-                    self.stream.write(log_contents)
-                    self.stream.write('\n')
-                    self.stream.write(
-                            ('^^^^[log from %s]' % test.id()).ljust(78,'-'))
-                    self.stream.write('\n')
-            self.stream.writeln(self.separator2)
-            self.stream.writeln("%s" % err)
-
     def _post_mortem(self):
         """Start a PDB post mortem session."""
         if os.environ.get('BZR_TEST_PDB', None):
@@ -602,6 +583,13 @@
             applied left to right - the first element in the list is the 
             innermost decorator.
         """
+        # stream may know claim to know to write unicode strings, but in older
+        # pythons this goes sufficiently wrong that it is a bad idea. (
+        # specifically a built in file with encoding 'UTF-8' will still try
+        # to encode using ascii.
+        new_encoding = osutils.get_terminal_encoding()
+        stream = codecs.getwriter(new_encoding)(stream)
+        stream.encoding = new_encoding
         self.stream = unittest._WritelnDecorator(stream)
         self.descriptions = descriptions
         self.verbosity = verbosity
@@ -670,6 +658,22 @@
     """
 
 
+# traceback._some_str fails to format exceptions that have the default
+# __str__ which does an implicit ascii conversion. However, repr() on those
+# objects works, for all that its not quite what the doctor may have ordered.
+def _clever_some_str(value):
+    try:
+        return str(value)
+    except:
+        try:
+            return repr(value).replace('\\n', '\n')
+        except:
+            return '<unprintable %s object>' % type(value).__name__
+
+traceback._some_str = _clever_some_str
+
+
+
 class KnownFailure(AssertionError):
     """Indicates that a test failed in a precisely expected manner.
 
@@ -798,15 +802,15 @@
             (TestNotApplicable, self._do_not_applicable))
         self.exception_handlers.insert(0,
             (KnownFailure, self._do_known_failure))
+
+    def setUp(self):
+        super(TestCase, self).setUp()
+        for feature in getattr(self, '_test_needs_features', []):
+            self.requireFeature(feature)
         self._log_contents = None
         self.addDetail("log", content.Content(content.ContentType("text",
             "plain", {"charset": "utf8"}),
-            lambda:[self._get_log(keep_log_file=True)]))
-
-    def setUp(self):
-        super(TestCase, self).setUp()
-        for feature in getattr(self, '_test_needs_features', []):
-            self.requireFeature(feature)
+            lambda:[self._get_log(keep_log_file=True, _utf8=True)]))
         self._cleanEnvironment()
         self._silenceUI()
         self._startLogFile()
@@ -1631,16 +1635,24 @@
     def log(self, *args):
         mutter(*args)
 
-    def _get_log(self, keep_log_file=False):
+    def _get_log(self, keep_log_file=False, _utf8=False):
         """Get the log from bzrlib.trace calls from this test.
 
         :param keep_log_file: When True, if the log is still a file on disk
             leave it as a file on disk. When False, if the log is still a file
             on disk, the log file is deleted and the log preserved as
             self._log_contents.
+        :param _utf8: Private for the getDetails callback; used to ensure that
+            the returned content is valid utf8.
         :return: A string containing the log.
         """
         if self._log_contents is not None:
+            if _utf8:
+                try:
+                    self._log_contents.decode('utf8')
+                except UnicodeDecodeError:
+                    unicodestr = self._log_contents.decode('utf8', 'replace')
+                    self._log_contents = unicodestr.encode('utf8')
             return self._log_contents
         import bzrlib.trace
         if bzrlib.trace._trace_file:
@@ -1652,6 +1664,12 @@
                 log_contents = logfile.read()
             finally:
                 logfile.close()
+            if _utf8:
+                try:
+                    log_contents.decode('utf8')
+                except UnicodeDecodeError:
+                    unicodestr = log_contents.decode('utf8', 'replace')
+                    log_contents = unicodestr.encode('utf8')
             if not keep_log_file:
                 # Permit multiple calls to get_log.
                 self._log_contents = log_contents
@@ -3075,7 +3093,7 @@
         if self.randomised:
             return iter(self._tests)
         self.randomised = True
-        self.stream.writeln("Randomizing test order using seed %s\n" %
+        self.stream.write("Randomizing test order using seed %s\n" %
             (self.actual_seed()))
         # Initialise the random number generator.
         random.seed(self.actual_seed())

=== modified file 'bzrlib/tests/blackbox/test_debug.py'
--- a/bzrlib/tests/blackbox/test_debug.py	2009-03-23 14:59:43 +0000
+++ b/bzrlib/tests/blackbox/test_debug.py	2009-12-06 00:12:45 +0000
@@ -38,5 +38,5 @@
     def test_dash_dlock(self):
         # With -Dlock, locking and unlocking is recorded into the log
         self.run_bzr("-Dlock init foo")
-        trace_messages = self._get_log(keep_log_file=True)
-        self.assertContainsRe(trace_messages, "lock_write")
+        log = u"".join(self.getDetails()['log'].iter_text())
+        self.assertContainsRe(log, "lock_write")

=== modified file 'bzrlib/tests/blackbox/test_exceptions.py'
--- a/bzrlib/tests/blackbox/test_exceptions.py	2009-08-20 06:25:02 +0000
+++ b/bzrlib/tests/blackbox/test_exceptions.py	2009-12-06 00:12:45 +0000
@@ -55,8 +55,8 @@
             os.mkdir('foo')
             bzrdir.BzrDirFormat5().initialize('foo')
             out, err = self.run_bzr("status foo")
-            self.assertContainsRe(self._get_log(keep_log_file=True),
-                                  "bzr upgrade")
+            log = u"".join(self.getDetails()['log'].iter_text())
+            self.assertContainsRe(log, "bzr upgrade")
         finally:
             repository._deprecation_warning_done = True
 

=== modified file 'bzrlib/tests/per_pack_repository.py'
--- a/bzrlib/tests/per_pack_repository.py	2009-09-07 03:35:06 +0000
+++ b/bzrlib/tests/per_pack_repository.py	2009-12-06 00:12:45 +0000
@@ -687,9 +687,9 @@
         # abort_write_group will not raise an error
         self.assertEqual(None, repo.abort_write_group(suppress_errors=True))
         # But it does log an error
-        log_file = self._get_log(keep_log_file=True)
-        self.assertContainsRe(log_file, 'abort_write_group failed')
-        self.assertContainsRe(log_file, r'INFO  bzr: ERROR \(ignored\):')
+        log = u"".join(self.getDetails()['log'].iter_text())
+        self.assertContainsRe(log, 'abort_write_group failed')
+        self.assertContainsRe(log, r'INFO  bzr: ERROR \(ignored\):')
         if token is not None:
             repo.leave_lock_in_place()
 

=== modified file 'bzrlib/tests/per_repository/test_check.py'
--- a/bzrlib/tests/per_repository/test_check.py	2009-08-30 23:51:10 +0000
+++ b/bzrlib/tests/per_repository/test_check.py	2009-12-06 00:12:45 +0000
@@ -46,7 +46,7 @@
         revid2 = tree.commit('2')
         check_object = tree.branch.repository.check([revid1, revid2])
         check_object.report_results(verbose=True)
-        log = self._get_log(keep_log_file=True)
+        log = u"".join(self.getDetails()['log'].iter_text())
         self.assertContainsRe(log, "0 unreferenced text versions")
 
 
@@ -90,11 +90,11 @@
         # contents of it!
         check_object = repo.check(['ignored'])
         check_object.report_results(verbose=False)
-        log = self._get_log(keep_log_file=True)
+        log = u"".join(self.getDetails()['log'].iter_text())
         self.assertContainsRe(
             log, '1 revisions have incorrect parents in the revision index')
         check_object.report_results(verbose=True)
-        log = self._get_log(keep_log_file=True)
+        log = u"".join(self.getDetails()['log'].iter_text())
         self.assertContainsRe(
             log,
             "revision-id has wrong parents in index: "
@@ -147,5 +147,5 @@
         rev_id = builder.commit('first post')
         result = repo.check(None, check_repo=True)
         result.report_results(True)
-        log = self._get_log(keep_log_file=True)
+        log = u"".join(self.getDetails()['log'].iter_text())
         self.assertFalse('Missing' in log, "Something was missing in %r" % log)

=== modified file 'bzrlib/tests/per_repository/test_check_reconcile.py'
--- a/bzrlib/tests/per_repository/test_check_reconcile.py	2009-03-23 14:59:43 +0000
+++ b/bzrlib/tests/per_repository/test_check_reconcile.py	2009-12-06 00:12:45 +0000
@@ -198,10 +198,9 @@
         repo, scenario = self.prepare_test_repository()
         check_result = repo.check()
         check_result.report_results(verbose=True)
+        log = u"".join(self.getDetails()['log'].iter_text())
         for pattern in scenario.check_regexes(repo):
-            self.assertContainsRe(
-                self._get_log(keep_log_file=True),
-                pattern)
+            self.assertContainsRe(log, pattern)
 
     def test_find_text_key_references(self):
         """Test that find_text_key_references finds erroneous references."""

=== modified file 'bzrlib/tests/per_repository_reference/test_check.py'
--- a/bzrlib/tests/per_repository_reference/test_check.py	2009-08-11 05:26:57 +0000
+++ b/bzrlib/tests/per_repository_reference/test_check.py	2009-12-06 00:12:45 +0000
@@ -39,5 +39,5 @@
         check_result = referring.branch.repository.check(
             referring.branch.repository.all_revision_ids())
         check_result.report_results(verbose=False)
-        log = self._get_log(keep_log_file=True)
+        log = u"".join(self.getDetails()['log'].iter_text())
         self.assertFalse("inconsistent parents" in log)

=== modified file 'bzrlib/tests/test_cleanup.py'
--- a/bzrlib/tests/test_cleanup.py	2009-10-26 06:23:14 +0000
+++ b/bzrlib/tests/test_cleanup.py	2009-12-06 00:12:45 +0000
@@ -39,7 +39,7 @@
         self.call_log.append('no_op_cleanup')
 
     def assertLogContains(self, regex):
-        log = self._get_log(keep_log_file=True)
+        log = u"".join(self.getDetails()['log'].iter_text())
         self.assertContainsRe(log, regex, re.DOTALL)
 
     def failing_cleanup(self):
@@ -184,7 +184,7 @@
         self.assertRaises(ErrorA, _do_with_cleanups, cleanups,
             self.trivial_func)
         self.assertLogContains('Cleanup failed:.*ErrorB')
-        log = self._get_log(keep_log_file=True)
+        log = u"".join(self.getDetails()['log'].iter_text())
         self.assertFalse('ErrorA' in log)
 
     def make_two_failing_cleanup_funcs(self):

=== modified file 'bzrlib/tests/test_config.py'
--- a/bzrlib/tests/test_config.py	2009-08-20 04:53:23 +0000
+++ b/bzrlib/tests/test_config.py	2009-12-06 00:12:45 +0000
@@ -1573,8 +1573,9 @@
         # the user is prompted
         self.assertEquals(entered_password,
                           conf.get_password('ssh', 'bar.org', user='jim'))
+        log = u"".join(self.getDetails()['log'].iter_text())
         self.assertContainsRe(
-            self._get_log(keep_log_file=True),
+            log,
             'password ignored in section \[ssh with password\]')
 
     def test_ssh_without_password_doesnt_emit_warning(self):
@@ -1598,8 +1599,9 @@
                           conf.get_password('ssh', 'bar.org', user='jim'))
         # No warning shoud be emitted since there is no password. We are only
         # providing "user".
+        log = u"".join(self.getDetails()['log'].iter_text())
         self.assertNotContainsRe(
-            self._get_log(keep_log_file=True),
+            log,
             'password ignored in section \[ssh with password\]')
 
     def test_uses_fallback_stores(self):

=== modified file 'bzrlib/tests/test_http_response.py'
--- a/bzrlib/tests/test_http_response.py	2009-03-23 14:59:43 +0000
+++ b/bzrlib/tests/test_http_response.py	2009-12-06 00:12:45 +0000
@@ -96,8 +96,8 @@
         # Override the thresold to force the warning emission
         conn._range_warning_thresold = 6 # There are 7 bytes pending
         conn.cleanup_pipe()
-        self.assertContainsRe(self._get_log(keep_log_file=True),
-                              'Got a 200 response when asking')
+        log = u"".join(self.getDetails()['log'].iter_text())
+        self.assertContainsRe(log, 'Got a 200 response when asking')
 
 
 class TestRangeFileMixin(object):

=== modified file 'bzrlib/tests/test_merge.py'
--- a/bzrlib/tests/test_merge.py	2009-09-19 00:32:14 +0000
+++ b/bzrlib/tests/test_merge.py	2009-12-06 00:12:45 +0000
@@ -151,12 +151,12 @@
         log = StringIO()
         merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),
                     this_tree=tree_b, ignore_zero=True)
-        log = self._get_log(keep_log_file=True)
+        log = u"".join(self.getDetails()['log'].iter_text())
         self.failUnless('All changes applied successfully.\n' not in log)
         tree_b.revert()
         merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),
                     this_tree=tree_b, ignore_zero=False)
-        log = self._get_log(keep_log_file=True)
+        log = u"".join(self.getDetails()['log'].iter_text())
         self.failUnless('All changes applied successfully.\n' in log)
 
     def test_merge_inner_conflicts(self):

=== modified file 'bzrlib/tests/test_remote.py'
--- a/bzrlib/tests/test_remote.py	2009-11-11 06:50:40 +0000
+++ b/bzrlib/tests/test_remote.py	2009-12-06 00:12:45 +0000
@@ -2892,8 +2892,9 @@
         self.assertEqual(server_error, translated_error)
         # In addition to re-raising ErrorFromSmartServer, some debug info has
         # been muttered to the log file for developer to look at.
+        log = u"".join(self.getDetails()['log'].iter_text())
         self.assertContainsRe(
-            self._get_log(keep_log_file=True),
+            log,
             "Missing key 'branch' in context")
 
     def test_path_missing(self):
@@ -2907,8 +2908,8 @@
         self.assertEqual(server_error, translated_error)
         # In addition to re-raising ErrorFromSmartServer, some debug info has
         # been muttered to the log file for developer to look at.
-        self.assertContainsRe(
-            self._get_log(keep_log_file=True), "Missing key 'path' in context")
+        log = u"".join(self.getDetails()['log'].iter_text())
+        self.assertContainsRe(log, "Missing key 'path' in context")
 
 
 class TestStacking(tests.TestCaseWithTransport):

=== modified file 'bzrlib/tests/test_trace.py'
--- a/bzrlib/tests/test_trace.py	2009-09-03 02:59:56 +0000
+++ b/bzrlib/tests/test_trace.py	2009-12-06 00:12:45 +0000
@@ -140,21 +140,21 @@
     def test_trace_unicode(self):
         """Write Unicode to trace log"""
         self.log(u'the unicode character for benzene is \N{BENZENE RING}')
-        self.assertContainsRe(self._get_log(keep_log_file=True),
-                              "the unicode character for benzene is")
+        log = u"".join(self.getDetails()['log'].iter_text())
+        self.assertContainsRe(log, "the unicode character for benzene is")
 
     def test_trace_argument_unicode(self):
         """Write a Unicode argument to the trace log"""
         mutter(u'the unicode character for benzene is %s', u'\N{BENZENE RING}')
-        self.assertContainsRe(self._get_log(keep_log_file=True),
-                              'the unicode character')
+        log = u"".join(self.getDetails()['log'].iter_text())
+        self.assertContainsRe(log, 'the unicode character')
 
     def test_trace_argument_utf8(self):
         """Write a Unicode argument to the trace log"""
         mutter(u'the unicode character for benzene is %s',
                u'\N{BENZENE RING}'.encode('utf-8'))
-        self.assertContainsRe(self._get_log(keep_log_file=True),
-                              'the unicode character')
+        log = u"".join(self.getDetails()['log'].iter_text())
+        self.assertContainsRe(log, 'the unicode character')
 
     def test_report_broken_pipe(self):
         try:
@@ -173,7 +173,7 @@
     def test_mutter_callsite_1(self):
         """mutter_callsite can capture 1 level of stack frame."""
         mutter_callsite(1, "foo %s", "a string")
-        log = self._get_log(keep_log_file=True)
+        log = u"".join(self.getDetails()['log'].iter_text())
         # begin with the message
         self.assertLogStartsWith(log, 'foo a string\nCalled from:\n')
         # should show two frame: this frame and the one above
@@ -185,7 +185,7 @@
     def test_mutter_callsite_2(self):
         """mutter_callsite can capture 2 levels of stack frame."""
         mutter_callsite(2, "foo %s", "a string")
-        log = self._get_log(keep_log_file=True)
+        log = u"".join(self.getDetails()['log'].iter_text())
         # begin with the message
         self.assertLogStartsWith(log, 'foo a string\nCalled from:\n')
         # should show two frame: this frame and the one above
@@ -197,13 +197,19 @@
     def test_mutter_never_fails(self):
         # Even if the decode/encode stage fails, mutter should not
         # raise an exception
+        # This test checks that mutter doesn't fail; the current behaviour
+        # is that it doesn't fail *and writes non-utf8*.
         mutter(u'Writing a greek mu (\xb5) works in a unicode string')
         mutter('But fails in an ascii string \xb5')
         mutter('and in an ascii argument: %s', '\xb5')
-        log = self._get_log(keep_log_file=True)
+        log = u"".join(self.getDetails()['log'].iter_text())
         self.assertContainsRe(log, 'Writing a greek mu')
         self.assertContainsRe(log, "But fails in an ascii string")
-        self.assertContainsRe(log, u"ascii argument: \xb5")
+        # However, the log content object does unicode replacement on reading
+        # to let it get unicode back where good data has been written. So we
+        # have to do a replaceent here as well.
+        self.assertContainsRe(log, "ascii argument: \xb5".decode('utf8',
+            'replace'))
 
     def test_push_log_file(self):
         """Can push and pop log file, and this catches mutter messages.

=== modified file 'bzrlib/tests/test_transport_log.py'
--- a/bzrlib/tests/test_transport_log.py	2009-07-01 07:04:47 +0000
+++ b/bzrlib/tests/test_transport_log.py	2009-12-06 00:12:45 +0000
@@ -36,10 +36,9 @@
         # operations such as mkdir are logged
         mutter('where are you?')
         logging_transport.mkdir('subdir')
-        self.assertContainsRe(self._get_log(True),
-            r'mkdir memory\+\d+://.*subdir')
-        self.assertContainsRe(self._get_log(True),
-            '  --> None')
+        log = u"".join(self.getDetails()['log'].iter_text())
+        self.assertContainsRe(log, r'mkdir memory\+\d+://.*subdir')
+        self.assertContainsRe(log, '  --> None')
         # they have the expected effect
         self.assertTrue(logging_transport.has('subdir'))
         # and they operate on the underlying transport




More information about the bazaar-commits mailing list