Merge lp:~lifeless/bzr/subunit into lp:bzr

Proposed by Robert Collins
Status: Merged
Merged at revision: not available
Proposed branch: lp:~lifeless/bzr/subunit
Merge into: lp:bzr
Diff against target: 1365 lines (+225/-497)
18 files modified
NEWS (+12/-0)
bzrlib/osutils.py (+15/-0)
bzrlib/tests/TestUtil.py (+12/-0)
bzrlib/tests/__init__.py (+98/-283)
bzrlib/tests/blackbox/test_debug.py (+1/-2)
bzrlib/tests/blackbox/test_exceptions.py (+1/-2)
bzrlib/tests/per_pack_repository.py (+3/-3)
bzrlib/tests/per_repository/test_check.py (+5/-8)
bzrlib/tests/per_repository/test_check_reconcile.py (+2/-3)
bzrlib/tests/per_repository_reference/test_check.py (+1/-2)
bzrlib/tests/test_cleanup.py (+2/-4)
bzrlib/tests/test_config.py (+2/-2)
bzrlib/tests/test_http_response.py (+1/-2)
bzrlib/tests/test_merge.py (+3/-4)
bzrlib/tests/test_remote.py (+2/-3)
bzrlib/tests/test_selftest.py (+46/-165)
bzrlib/tests/test_trace.py (+16/-10)
bzrlib/tests/test_transport_log.py (+3/-4)
To merge this branch: bzr merge lp:~lifeless/bzr/subunit
Reviewer Review Type Date Requested Status
Martin Packman (community) Abstain
Vincent Ladeuil Needs Fixing
Martin Pool Needs Fixing
Review via email: mp+15704@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

I'm proposing this branch to get feedback; its not necessarily ripe, though I'd be happy to land it as-is (there aren't any must-do items remaining for it).

This branch deletes a bunch of code:
 17 files changed, 180 insertions(+), 350 deletions(-)

It dos this by using testtools as a base class and using the defined extension points there to add bzr specific functionality.

The net effect of doing this:
 - bzr selftest --parallel will now show log files, as will --subunit and pqm if we enable subunit there again.
 - assertThat is available for bzr to use
 - bzr no longer needs an implementation of run(), addCleanup(). There may be more duplicate assertions, but I haven't been on a hunt to minimise the duplication.

testtools 0.10 will be needed (that isn't released yet, but jml wants to bump to -10 when we make run() extensible. I'm putting a branch for that up for review in a few minutes).

-Rob

Revision history for this message
Martin Pool (mbp) wrote :

2009/12/6 Robert Collins <email address hidden>:
> I'm proposing this branch to get feedback; its not necessarily ripe, though I'd be happy to land it as-is (there aren't any must-do items remaining for it).

Well done.

> === modified file 'NEWS'
> --- NEWS        2009-12-04 22:13:52 +0000
> +++ NEWS        2009-12-06 03:05:32 +0000
> @@ -237,6 +237,10 @@
>  Testing
>  *******
>
> +* ``bzrlib.tests.TestCase`` now subclasses ``testtools.testcase.TestCase``.
> +  This permits features in testtools such as getUniqueInteger and
> +  getUniqueString to be used. (Robert Collins)
> +
>  * -Dhpssvfs will now trigger on ``RemoteBzrDir._ensure_real``, providing
>   more debugging of VFS access triggers. (Robert Collins)

You really need to explicitly mention that testtools is now a
requirement for running the Bazaar test suite, and which version, and
preferably also the URL. It seems like this should also update the
install instructions and maybe setup.py?

Also worth checking that you get a sane message if it's missing.

Since testtools will be changing I'd think about putting in a version
number check after the import.

More later maybe.

--
Martin <http://launchpad.net/~mbp/>

Revision history for this message
Robert Collins (lifeless) wrote :

I've added some polish and improved the news entry as per Martin's request.

Revision history for this message
Martin Packman (gz) wrote :

Doesn't seem as big a code complexity win as I'd hope for adding a whole new dependency.

+traceback._some_str = _clever_some_str

Not sure monkeypatching a builtin module in the middle of a several thousand line script is a good idea.

Revision history for this message
Robert Collins (lifeless) wrote :

On Mon, 2009-12-14 at 21:38 +0000, Martin [gz] wrote:
> Doesn't seem as big a code complexity win as I'd hope for adding a whole new dependency.

How big is big enough ?

> +traceback._some_str = _clever_some_str
>
> Not sure monkeypatching a builtin module in the middle of a several thousand line script is a good idea.

Sadly, python 2.x is broken in this regard: __str__ on an exception with
a (a_str,) as its args, does an implicit decode of the string. We work
around this in bzrlib.errors by subclassing exception and reimplementing
__str__, but we can't do that for all errors that may be raised by
tests.

-Rob

Revision history for this message
Robert Collins (lifeless) wrote :

I plan to land this this afternoon or perhaps tomorrow. Testtools 0.9.2 has been released, and the package is in debian. I can upload that to e.g. the bzr beta PPA.

There is a subunit 0.0.3 release as well, which makes --parallel=fork, ec2test and plain --subunit include separate attachments for the log files (but the fallback behaviour in testtools will include those anyway just not structured as such.

Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Robert Collins wrote:
> I plan to land this this afternoon or perhaps tomorrow. Testtools 0.9.2 has been released, and the package is in debian. I can upload that to e.g. the bzr beta PPA.
>
> There is a subunit 0.0.3 release as well, which makes --parallel=fork, ec2test and plain --subunit include separate attachments for the log files (but the fallback behaviour in testtools will include those anyway just not structured as such.

Do you have any idea whether this will destroy the win32 test suite? Or
mac, or anything that doesn't have a deb package infrastructure? I
realize you are antsy to move forward, but given I just got win32
running again, I'd rather not have a serious breakdown at this point. (I
expect it to *probably* be ok, if you can get the right packages installed.)

The comments you've received certainly haven't been "this looks good,
let's land it", so I'm a bit surprised you are pushing this hard. Maybe
I'm wrong, but it feels like the conversation hasn't finished yet.

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAksoTYIACgkQJdeBCYSNAAPGgACgson2YfKfVf+Qs8r0DpDMCJ3A
GiwAn3Jc9fkaAe229h9Kmbxo/aHdahrU
=tUCZ
-----END PGP SIGNATURE-----

Revision history for this message
Gordon Tyler (doxxx) wrote :

After upgrading to the latest testtools and subunit, I'm able to run bzr selftest using this proposed merge. Still waiting for it to finish though.

Revision history for this message
Gordon Tyler (doxxx) wrote :

Perhaps I should mention that I'm running this on Vista64 using a 32-bit version of ActivePython 2.6.

Revision history for this message
Robert Collins (lifeless) wrote :

On Wed, 2009-12-16 at 03:03 +0000, John A Meinel wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Robert Collins wrote:
> > I plan to land this this afternoon or perhaps tomorrow. Testtools 0.9.2 has been released, and the package is in debian. I can upload that to e.g. the bzr beta PPA.
> >
> > There is a subunit 0.0.3 release as well, which makes --parallel=fork, ec2test and plain --subunit include separate attachments for the log files (but the fallback behaviour in testtools will include those anyway just not structured as such.
>
> Do you have any idea whether this will destroy the win32 test suite?

Yes, it won't.

> Or
> mac, or anything that doesn't have a deb package infrastructure? I
> realize you are antsy to move forward, but given I just got win32
> running again, I'd rather not have a serious breakdown at this point. (I
> expect it to *probably* be ok, if you can get the right packages installed.)

All you need is python-testtools (available via pypi, easy_install,
launchpad tarball or bzr branch lp:testtools), and python
2.4/2.5/2.6/2.7/3.1/3.2.

> The comments you've received certainly haven't been "this looks good,
> let's land it", so I'm a bit surprised you are pushing this hard. Maybe
> I'm wrong, but it feels like the conversation hasn't finished yet.

Martin Pool provided some comments, which I've considered and acted on
most of. He said 'maybe more later', that was 9 days ago; Martin [gz]
said 'not as much benefit as expected', and I've asked how much he
expected.

I have no sense of ongoing discussion about this; its been blocked on
testtools releasing an appropriate release - which it has now, and given
the absence of negative reviews, am planning on exercising the soft
timeout our review process has rather than delaying indefinitely. There
has been discussion on list about the general issue of dependencies, and
the outcome there was 'put it up and we can discuss it' (as I recall) -
which is what I've done. I also recall Martin (Pool) saying somewhere
that he is convinced that testtools has useful features and active
maintainers and he is ok with a dependency on it.

Of course, if you are concerned about it I'll hold off landing it, but
I'd like some articulation of concerns so that I can act on them. This
is important to me because I don't like writing code twice, and bzr's
current unittest structure forces me to do so.

This patch doesn't do any OS specific changes, so its very unlikely to
have an OS specific fallout. Gordon's positive feedback is an indicator
that its probably ok.

-Rob

Revision history for this message
Martin Pool (mbp) wrote :

2009/12/16 Robert Collins <email address hidden>:
> Martin Pool provided some comments, which I've considered and acted on
> most of. He said 'maybe more later', that was 9 days ago;

I'm not +1 until I get to read it again - the maybe was whether I
would get to that soon. If other core reviewers approve before I do,
that's great.

--
Martin <http://launchpad.net/~mbp/>

Revision history for this message
Robert Collins (lifeless) wrote :

On Wed, 2009-12-16 at 04:57 +0000, Martin Pool wrote:
> 2009/12/16 Robert Collins <email address hidden>:
> > Martin Pool provided some comments, which I've considered and acted on
> > most of. He said 'maybe more later', that was 9 days ago;
>
> I'm not +1 until I get to read it again - the maybe was whether I
> would get to that soon. If other core reviewers approve before I do,
> that's great.

Given your encouragement to us to not block, do you think 10 days is
'long enough' to land in the absence of negative feedback?

Its not even a particularly large patch - 18 files changed, 210
insertions(+), 412 deletions(-) - so I feel that the lack of a full
review is an indication that its not important in either a risk or
benefits sense to other folk.

As I said to John, I won't land it if folk have concerns, but neither do
I want to block on it simply because folk aren't excited enough to
review it.

-Rob

Revision history for this message
Matt Nordhoff (mnordhoff) wrote :

Matt's tiny nitpicks of the day:

25 === modified file 'bzrlib/osutils.py'

42 + if type(object) == str:

Types should be compared with "is".

71 === modified file 'bzrlib/tests/__init__.py'

156 + # pythons this goes sufficiently wrong that it is a bad idea. (
157 + # specifically a built in file with encoding 'UTF-8' will still try
158 + # to encode using ascii.

Where's the ")"? :D

198 +def _clever_some_str(value):
199 + try:
200 + return str(value)
201 + except:

Blanket except? That's scary.

290 + @staticmethod
291 + def _do_known_failure(self, result, e):

299 + @staticmethod
300 def _do_not_applicable(self, result, e):

308 + @staticmethod
309 + def _do_unsupported_or_skip(self, result, e):

I should probably read the testtools docs or something, but... Static methods that take "self"?

597 === modified file 'bzrlib/tests/blackbox/test_debug.py'

606 + log = u"".join(self.getDetails()['log'].iter_text())

It's a bit unpleasant to have that pasted all over the test suite. Though I guess it isn't duplicated *that* many times, given the number of tests...

Revision history for this message
Martin Pool (mbp) wrote :

8 +* New helper osutils.StreamWriter which encodes unicode objects but
9 + passes str objects straight through. This is used for selftest but
10 + may be useful for diff and other operations that generate mixed output.
11 + (Robert Collins)
12 +

Please let's not have two things called StreamWriter that behave differently. needsfixing. Something like UnicodeOrBytesToBytesWriter.

+ log = u"".join(self.getDetails()['log'].iter_text())

Agree with Matt, this is ugly to repeat. Couldn't we have a convenience function for it?

review: Needs Fixing
Revision history for this message
Robert Collins (lifeless) wrote :

On Wed, 2009-12-16 at 06:05 +0000, Matt Nordhoff wrote:
>
> 198 +def _clever_some_str(value):
> 199 + try:
> 200 + return str(value)
> 201 + except:
>
> Blanket except? That's scary.

Look at traceback._some_str :P.

-Rob

Revision history for this message
Robert Collins (lifeless) wrote :

On Wed, 2009-12-16 at 06:09 +0000, Martin Pool wrote:
> Review: Needs Fixing
> 8 +* New helper osutils.StreamWriter which encodes unicode objects but
> 9 + passes str objects straight through. This is used for selftest but
> 10 + may be useful for diff and other operations that generate mixed output.
> 11 + (Robert Collins)
> 12 +
>
> Please let's not have two things called StreamWriter that behave differently. needsfixing. Something like UnicodeOrBytesToBytesWriter.

Ok. I had called it StreamWriter because I plan to submit a patch to
python to fix this, as I believe it meets the /intent/ of the python
code more accurately, but I'm happy to rename it for now.

> + log = u"".join(self.getDetails()['log'].iter_text())
>
> Agree with Matt, this is ugly to repeat. Couldn't we have a convenience function for it?

Sure, I was intent on getting away from the tricky implementation we
had, but having done so can add back a get_log() happily. I'll do this
tomorrow.

-Rob

Revision history for this message
Vincent Ladeuil (vila) wrote :

So. I've tested without subunit nor testtools => clean failure:

bzr: ERROR: No module named testtools
You may need to install this Python library separately.

We may want to polish things a bit on platforms where this may be harder to install, but nothing urgent.

I've tested with my current versions of subunit/testtools, horrible crashes :) The NEWS entry should
take care of that (I don't know how many people are currently using testtools/subunit and running
the test suite but they are likely to read the NEWS).

BUT, running with up-to-date versions is far too verbose for XFAILs, the traceback is nicely presented
thank you, I just don't want to see it when running with --parallel=fork.

I'm +1 for get_log() and renaming StreamWriter until your patch is accepted.

That being said, it's a pleasure to see many warts removed (don't tell me you need BZRTransformingResult
back to addres the above :0) , thanks for that.

I'll be running this branch on babune and comment later about the results.

review: Needs Fixing
Revision history for this message
Vincent Ladeuil (vila) wrote :

babune is still running but at least one full test suite has run all tracked OSes (OSX still suspended)
So, there is nothing important to add except that skipped tests are also too verbose:

per_branch.test_bound_sftp.BoundSFTPBranch.test_unbinding(BzrBranchFormat6) SKIP 8ms
Text attachment: log
------------

------------
Text attachment: reason
------------
Not a local URL
------------

Hmm, weird, I don't get the same behavior everywhere, some runs handle XFAIL but not SKIP, some handle neither...

Revision history for this message
Robert Collins (lifeless) wrote :

On Wed, 2009-12-16 at 15:44 +0000, Vincent Ladeuil wrote:
>
> I've tested with my current versions of subunit/testtools, horrible
> crashes :) The NEWS entry should
> take care of that (I don't know how many people are currently using
> testtools/subunit and running
> the test suite but they are likely to read the NEWS).

It would help me when reporting a problem with a patch if you can paste
what it looks like, then I can describe that easily for other people.

> BUT, running with up-to-date versions is far too verbose for XFAILs,
> the traceback is nicely presented
> thank you, I just don't want to see it when running with
> --parallel=fork.

Oh, that was an unintended behavioural change, will fix. It is detecting
it as xfail FWIW.

-Rob

Revision history for this message
Robert Collins (lifeless) wrote :

On Wed, 2009-12-16 at 20:44 +0000, Vincent Ladeuil wrote:
> babune is still running but at least one full test suite has run all tracked OSes (OSX still suspended)
> So, there is nothing important to add except that skipped tests are also too verbose:

What platform :- ?

./bzr selftest --no-plugins
per_branch.test_bound_sftp.BoundSFTPBranch.test_unbinding
--parallel=fork
bzr
selftest: /home/robertc/source/baz/pending/working/bzr
   /home/robertc/source/baz/pending/working/bzrlib
   bzr-2.1.0dev5 python-2.6.4
Linux-2.6.31-16-generic-x86_64-with-Ubuntu-9.10-karmic

----------------------------------------------------------------------
Ran 8 tests in 0.630s

OK
8 tests skipped

Revision history for this message
Robert Collins (lifeless) wrote :

On Wed, 2009-12-16 at 21:00 +0000, Robert Collins wrote:
>
> > BUT, running with up-to-date versions is far too verbose for XFAILs,
> > the traceback is nicely presented
> > thank you, I just don't want to see it when running with
> > --parallel=fork.
>
> Oh, that was an unintended behavioural change, will fix. It is
> detecting
> it as xfail FWIW.

Hah! Brilliant!

Read the code for addExpectedFailure

it is *meant* to show the backtrace.

But it doesn't. Why? because it uses ui.ui_factory.note, and during the
execution of a test (which is when the current code reports things with
exceptions), ui.ui_factory is a SilentUIFactory.

Global State, I heart thee.

Oh, I've also been able to delete expectFailure, another helper thats in
the base class I missed.

-Rob

Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Robert Collins wrote:
> On Wed, 2009-12-16 at 21:00 +0000, Robert Collins wrote:
>>> BUT, running with up-to-date versions is far too verbose for XFAILs,
>>> the traceback is nicely presented
>>> thank you, I just don't want to see it when running with
>>> --parallel=fork.
>> Oh, that was an unintended behavioural change, will fix. It is
>> detecting
>> it as xfail FWIW.
>
> Hah! Brilliant!
>
> Read the code for addExpectedFailure
>
> it is *meant* to show the backtrace.
>
> But it doesn't. Why? because it uses ui.ui_factory.note, and during the
> execution of a test (which is when the current code reports things with
> exceptions), ui.ui_factory is a SilentUIFactory.
>
> Global State, I heart thee.
>
> Oh, I've also been able to delete expectFailure, another helper thats in
> the base class I missed.
>
>
> -Rob

I'll note that I personally find the expectFailure syntax to be
backwards. At least, IMO, you should have an assert which *succeeds*
which proves that the failure is occurring. Rather than expecting the
assert to fail. Because the latter case means that it can fail for any
reason, and still cause the expectFailure to pass.

But, I guess we have what we have.
John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAkspgTQACgkQJdeBCYSNAANxwACePhFkg+V01paJ363ziq6X3PCv
27QAoKW9xy6Mt+JD1fGLoEEX0hfO/LDK
=ycMf
-----END PGP SIGNATURE-----

Revision history for this message
Martin Pool (mbp) wrote :

2009/12/17 John A Meinel <email address hidden>:
> I'll note that I personally find the expectFailure syntax to be
> backwards. At least, IMO, you should have an assert which *succeeds*
> which proves that the failure is occurring. Rather than expecting the
> assert to fail. Because the latter case means that it can fail for any
> reason, and still cause the expectFailure to pass.
>
> But, I guess we have what we have.

I don't mind renaming it or adding an alternative.

What I liked about expectFailure (and I think I added it) is that you
can just add it as a kind of preposition once you have a failing test.
 It's true it's less precise.

--
Martin <http://launchpad.net/~mbp/>

Revision history for this message
Robert Collins (lifeless) wrote :

Its actually pretty precise - it has to raise AssertionError : there are
three possibly behaviours:
 - the predicate works -> test failure
 - the predicate raises AssertionError -> test pass
 - the predicate raises some other exception -> test failure

-Rob

Revision history for this message
Martin Packman (gz) wrote :

> Martin [gz]
> said 'not as much benefit as expected', and I've asked how much he
> expected.

Sorry, launchpad seems not to send email if you only comment, so unless I happen to check back I miss things.

> How big is big enough ?

Well, currently when my code that depends on bazaar.tests breaks I have to read a 4500 line file to understand why. With this change, I'll have to read a 4300 line file and a new module I've never looked at before.

> Sadly, python 2.x is broken in this regard:

Sure, but is this really the best place to fix it? And when it's core python being fiddled with you expect at least an upstream bug report linked prominently near the fiddling.

Revision history for this message
Robert Collins (lifeless) wrote :

On Thu, 2009-12-17 at 02:17 +0000, Martin [gz] wrote:
> > Martin [gz]
> > said 'not as much benefit as expected', and I've asked how much he
> > expected.
>
> Sorry, launchpad seems not to send email if you only comment, so unless I happen to check back I miss things.

It should be mailing on every review-or-comment.

> > How big is big enough ?
>
> Well, currently when my code that depends on bazaar.tests breaks I have to read a 4500 line file to understand why. With this change, I'll have to read a 4300 line file and a new module I've never looked at before.
>
> > Sadly, python 2.x is broken in this regard:
>
> Sure, but is this really the best place to fix it? And when it's core python being fiddled with you expect at least an upstream bug report linked prominently near the fiddling.

If you have a better place to suggest, I'm happy to do it there.

python2.x won't be fixed in this regard, and 3.0 doesn't have the same
confusion AIUI.

-Rob

Revision history for this message
Martin Packman (gz) wrote :

> It should be mailing on every review-or-comment.

Hm, still no email. There's a nice big box on the page saying who's subscribed, and not button saying "add me" either. I'll set the review type this time which should get me cc-ed.

> If you have a better place to suggest, I'm happy to do it there.

Well, as printing exceptions is a testing kind of thing, the new upstream testing package?

Just tried installing the testtools package, it has three failures on Jython and doesn't install on IronPython. Probably shallow, I'll find some time to look into it.

review: Abstain
Revision history for this message
Matt Nordhoff (mnordhoff) wrote :

Martin [gz] wrote:
> Review: Abstain
>> It should be mailing on every review-or-comment.
>
> Hm, still no email. There's a nice big box on the page saying who's subscribed, and not button saying "add me" either. I'll set the review type this time which should get me cc-ed.

Yer still not on the list. Try subscribing to the source or destination
branch:

<https://code.launchpad.net/~lifeless/bzr/subunit>
<https://code.launchpad.net/~bzr-pqm/bzr/bzr.dev>
--
Matt Nordhoff

Revision history for this message
Robert Collins (lifeless) wrote :

On Thu, 2009-12-17 at 02:47 +0000, Martin [gz] wrote:
> Review: Abstain
> > It should be mailing on every review-or-comment.
>
> Hm, still no email. There's a nice big box on the page saying who's subscribed, and not button saying "add me" either. I'll set the review type this time which should get me cc-ed.
>
> > If you have a better place to suggest, I'm happy to do it there.
>
> Well, as printing exceptions is a testing kind of thing, the new upstream testing package?

Its only needed if you have exceptions that aren't valid [default codec]
- its not something I'd want just importing a regular library package to
do, but I think its ok for an application to do.

> Just tried installing the testtools package, it has three failures on Jython and doesn't install on IronPython. Probably shallow, I'll find some time to look into it.

Please report bugs at launchpad.net/testtools - will be happy to look at
them.

-Rob

Revision history for this message
Martin Pool (mbp) wrote :

2009/12/17 Martin [gz] <email address hidden>:
> Review: Abstain
>> It should be mailing on every review-or-comment.
>
> Hm, still no email. There's a nice big box on the page saying who's subscribed, and not button saying "add me" either. I'll set the review type this time which should get me cc-ed.

See https://bugs.edge.launchpad.net/launchpad-code/+bug/497617

--
Martin <http://launchpad.net/~mbp/>

Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Robert Collins wrote:
> Its actually pretty precise - it has to raise AssertionError : there are
> three possibly behaviours:
> - the predicate works -> test failure
> - the predicate raises AssertionError -> test pass
> - the predicate raises some other exception -> test failure
>
> -Rob

So that is true, except you write the test as 'assertEqual, 20' # fails

Which means that it could be 30, or it could be 100. When it often
should be failing in a known way. I think you can turn it around as
"assertNotEqual, 52". Though I had to think about it for a while, which
shows it isn't just obvious.

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAkspt/cACgkQJdeBCYSNAAOUSwCeJZS2heN4ZK0chKYVAXN/Zye5
RmgAoNG44xgHx+DSFn6PWhl7B/Rb9kXZ
=4Kc+
-----END PGP SIGNATURE-----

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2009-12-16 20:01:49 +0000
3+++ NEWS 2009-12-17 09:43:14 +0000
4@@ -38,9 +38,21 @@
5 Internals
6 *********
7
8+* New helper osutils.UnicodeOrBytesToBytesWriter which encodes unicode
9+ objects but passes str objects straight through. This is used for
10+ selftest but may be useful for diff and other operations that generate
11+ mixed output. (Robert Collins)
12+
13 Testing
14 *******
15
16+* ``bzrlib.tests.TestCase`` now subclasses ``testtools.testcase.TestCase``.
17+ This permits features in testtools such as getUniqueInteger and
18+ getUniqueString to be used. Because of this, testtools version 0.9.2 or
19+ newer is now a dependency to run bzr selftest. Running with versions of
20+ testtools less than 0.9.2 will cause bzr to error while loading the test
21+ suite. (Robert Collins)
22+
23 bzr 2.0.4 (not released yet)
24 ############################
25
26
27=== modified file 'bzrlib/osutils.py'
28--- bzrlib/osutils.py 2009-12-16 10:54:55 +0000
29+++ bzrlib/osutils.py 2009-12-17 09:43:14 +0000
30@@ -2087,3 +2087,18 @@
31 if use_cache:
32 _cached_concurrency = concurrency
33 return concurrency
34+
35+
36+class UnicodeOrBytesToBytesWriter(codecs.StreamWriter):
37+ """A stream writer that doesn't decode str arguments."""
38+
39+ def __init__(self, codec, stream, errors='strict'):
40+ codecs.StreamWriter.__init__(self, stream, errors)
41+ self.encode = codec.encode
42+
43+ def write(self, object):
44+ if type(object) is str:
45+ self.stream.write(object)
46+ else:
47+ data, _ = self.encode(object, self.errors)
48+ self.stream.write(data)
49
50=== modified file 'bzrlib/tests/TestUtil.py'
51--- bzrlib/tests/TestUtil.py 2009-03-23 14:59:43 +0000
52+++ bzrlib/tests/TestUtil.py 2009-12-17 09:43:14 +0000
53@@ -72,6 +72,18 @@
54 visitor.visitSuite(self)
55 visitTests(self, visitor)
56
57+ def run(self, result):
58+ """Run the tests in the suite, discarding references after running."""
59+ tests = list(self)
60+ tests.reverse()
61+ self._tests = []
62+ while tests:
63+ if result.shouldStop:
64+ self._tests = reversed(tests)
65+ break
66+ tests.pop().run(result)
67+ return result
68+
69
70 class TestLoader(unittest.TestLoader):
71 """Custom TestLoader to extend the stock python one."""
72
73=== modified file 'bzrlib/tests/__init__.py'
74--- bzrlib/tests/__init__.py 2009-12-08 21:46:07 +0000
75+++ bzrlib/tests/__init__.py 2009-12-17 09:43:14 +0000
76@@ -46,9 +46,12 @@
77 import tempfile
78 import threading
79 import time
80+import traceback
81 import unittest
82 import warnings
83
84+import testtools
85+from testtools import content
86
87 from bzrlib import (
88 branchbuilder,
89@@ -228,8 +231,10 @@
90 '%d non-main threads were left active in the end.\n'
91 % (TestCase._active_threads - 1))
92
93- def _extractBenchmarkTime(self, testCase):
94+ def _extractBenchmarkTime(self, testCase, details=None):
95 """Add a benchmark time for the current test case."""
96+ if details and 'benchtime' in details:
97+ return float(''.join(details['benchtime'].iter_bytes()))
98 return getattr(testCase, "_benchtime", None)
99
100 def _elapsedTestTimeString(self):
101@@ -269,7 +274,7 @@
102 else:
103 bzr_path = sys.executable
104 self.stream.write(
105- 'testing: %s\n' % (bzr_path,))
106+ 'bzr selftest: %s\n' % (bzr_path,))
107 self.stream.write(
108 ' %s\n' % (
109 bzrlib.__path__[0],))
110@@ -320,13 +325,13 @@
111 self.stop()
112 self._cleanupLogFile(test)
113
114- def addSuccess(self, test):
115+ def addSuccess(self, test, details=None):
116 """Tell result that test completed successfully.
117
118 Called from the TestCase run()
119 """
120 if self._bench_history is not None:
121- benchmark_time = self._extractBenchmarkTime(test)
122+ benchmark_time = self._extractBenchmarkTime(test, details)
123 if benchmark_time is not None:
124 self._bench_history.write("%s %s\n" % (
125 self._formatTime(benchmark_time),
126@@ -362,26 +367,6 @@
127 self.not_applicable_count += 1
128 self.report_not_applicable(test, reason)
129
130- def printErrorList(self, flavour, errors):
131- for test, err in errors:
132- self.stream.writeln(self.separator1)
133- self.stream.write("%s: " % flavour)
134- self.stream.writeln(self.getDescription(test))
135- if getattr(test, '_get_log', None) is not None:
136- log_contents = test._get_log()
137- if log_contents:
138- self.stream.write('\n')
139- self.stream.write(
140- ('vvvv[log from %s]' % test.id()).ljust(78,'-'))
141- self.stream.write('\n')
142- self.stream.write(log_contents)
143- self.stream.write('\n')
144- self.stream.write(
145- ('^^^^[log from %s]' % test.id()).ljust(78,'-'))
146- self.stream.write('\n')
147- self.stream.writeln(self.separator2)
148- self.stream.writeln("%s" % err)
149-
150 def _post_mortem(self):
151 """Start a PDB post mortem session."""
152 if os.environ.get('BZR_TEST_PDB', None):
153@@ -499,8 +484,7 @@
154 ))
155
156 def report_known_failure(self, test, err):
157- ui.ui_factory.note('XFAIL: %s\n%s\n' % (
158- self._test_description(test), err[1]))
159+ pass
160
161 def report_skip(self, test, reason):
162 pass
163@@ -604,6 +588,14 @@
164 applied left to right - the first element in the list is the
165 innermost decorator.
166 """
167+ # stream may know claim to know to write unicode strings, but in older
168+ # pythons this goes sufficiently wrong that it is a bad idea. (
169+ # specifically a built in file with encoding 'UTF-8' will still try
170+ # to encode using ascii.
171+ new_encoding = osutils.get_terminal_encoding()
172+ codec = codecs.lookup(new_encoding)
173+ stream = osutils.UnicodeOrBytesToBytesWriter(codec, stream)
174+ stream.encoding = new_encoding
175 self.stream = unittest._WritelnDecorator(stream)
176 self.descriptions = descriptions
177 self.verbosity = verbosity
178@@ -629,14 +621,6 @@
179 for decorator in self._result_decorators:
180 result = decorator(result)
181 result.stop_early = self.stop_on_failure
182- try:
183- import testtools
184- except ImportError:
185- pass
186- else:
187- if isinstance(test, testtools.ConcurrentTestSuite):
188- # We need to catch bzr specific behaviors
189- result = BZRTransformingResult(result)
190 result.startTestRun()
191 try:
192 test.run(result)
193@@ -660,8 +644,7 @@
194 % (type(suite), suite))
195
196
197-class TestSkipped(Exception):
198- """Indicates that a test was intentionally skipped, rather than failing."""
199+TestSkipped = testtools.testcase.TestSkipped
200
201
202 class TestNotApplicable(TestSkipped):
203@@ -673,14 +656,23 @@
204 """
205
206
207-class KnownFailure(AssertionError):
208- """Indicates that a test failed in a precisely expected manner.
209-
210- Such failures dont block the whole test suite from passing because they are
211- indicators of partially completed code or of future work. We have an
212- explicit error for them so that we can ensure that they are always visible:
213- KnownFailures are always shown in the output of bzr selftest.
214- """
215+# traceback._some_str fails to format exceptions that have the default
216+# __str__ which does an implicit ascii conversion. However, repr() on those
217+# objects works, for all that its not quite what the doctor may have ordered.
218+def _clever_some_str(value):
219+ try:
220+ return str(value)
221+ except:
222+ try:
223+ return repr(value).replace('\\n', '\n')
224+ except:
225+ return '<unprintable %s object>' % type(value).__name__
226+
227+traceback._some_str = _clever_some_str
228+
229+
230+# deprecated - use self.knownFailure(), or self.expectFailure.
231+KnownFailure = testtools.testcase._ExpectedFailure
232
233
234 class UnavailableFeature(Exception):
235@@ -762,7 +754,7 @@
236 return NullProgressView()
237
238
239-class TestCase(unittest.TestCase):
240+class TestCase(testtools.TestCase):
241 """Base class for bzr unit tests.
242
243 Tests that need access to disk resources should subclass
244@@ -787,24 +779,26 @@
245 _leaking_threads_tests = 0
246 _first_thread_leaker_id = None
247 _log_file_name = None
248- _log_contents = ''
249- _keep_log_file = False
250 # record lsprof data when performing benchmark calls.
251 _gather_lsprof_in_benchmarks = False
252- attrs_to_keep = ('id', '_testMethodName', '_testMethodDoc',
253- '_log_contents', '_log_file_name', '_benchtime',
254- '_TestCase__testMethodName', '_TestCase__testMethodDoc',)
255
256 def __init__(self, methodName='testMethod'):
257 super(TestCase, self).__init__(methodName)
258 self._cleanups = []
259- self._bzr_test_setUp_run = False
260- self._bzr_test_tearDown_run = False
261 self._directory_isolation = True
262+ self.exception_handlers.insert(0,
263+ (UnavailableFeature, self._do_unsupported_or_skip))
264+ self.exception_handlers.insert(0,
265+ (TestNotApplicable, self._do_not_applicable))
266
267 def setUp(self):
268- unittest.TestCase.setUp(self)
269- self._bzr_test_setUp_run = True
270+ super(TestCase, self).setUp()
271+ for feature in getattr(self, '_test_needs_features', []):
272+ self.requireFeature(feature)
273+ self._log_contents = None
274+ self.addDetail("log", content.Content(content.ContentType("text",
275+ "plain", {"charset": "utf8"}),
276+ lambda:[self._get_log(keep_log_file=True)]))
277 self._cleanEnvironment()
278 self._silenceUI()
279 self._startLogFile()
280@@ -1289,41 +1283,6 @@
281 m += ": " + msg
282 self.fail(m)
283
284- def expectFailure(self, reason, assertion, *args, **kwargs):
285- """Invoke a test, expecting it to fail for the given reason.
286-
287- This is for assertions that ought to succeed, but currently fail.
288- (The failure is *expected* but not *wanted*.) Please be very precise
289- about the failure you're expecting. If a new bug is introduced,
290- AssertionError should be raised, not KnownFailure.
291-
292- Frequently, expectFailure should be followed by an opposite assertion.
293- See example below.
294-
295- Intended to be used with a callable that raises AssertionError as the
296- 'assertion' parameter. args and kwargs are passed to the 'assertion'.
297-
298- Raises KnownFailure if the test fails. Raises AssertionError if the
299- test succeeds.
300-
301- example usage::
302-
303- self.expectFailure('Math is broken', self.assertNotEqual, 54,
304- dynamic_val)
305- self.assertEqual(42, dynamic_val)
306-
307- This means that a dynamic_val of 54 will cause the test to raise
308- a KnownFailure. Once math is fixed and the expectFailure is removed,
309- only a dynamic_val of 42 will allow the test to pass. Anything other
310- than 54 or 42 will cause an AssertionError.
311- """
312- try:
313- assertion(*args, **kwargs)
314- except AssertionError:
315- raise KnownFailure(reason)
316- else:
317- self.fail('Unexpected success. Should have failed: %s' % reason)
318-
319 def assertFileEqual(self, content, path):
320 """Fail if path does not contain 'content'."""
321 self.failUnlessExists(path)
322@@ -1479,18 +1438,12 @@
323
324 Close the file and delete it, unless setKeepLogfile was called.
325 """
326- if self._log_file is None:
327- return
328+ if bzrlib.trace._trace_file:
329+ # flush the log file, to get all content
330+ bzrlib.trace._trace_file.flush()
331 bzrlib.trace.pop_log_file(self._log_memento)
332- self._log_file.close()
333- self._log_file = None
334- if not self._keep_log_file:
335- os.remove(self._log_file_name)
336- self._log_file_name = None
337-
338- def setKeepLogfile(self):
339- """Make the logfile not be deleted when _finishLogFile is called."""
340- self._keep_log_file = True
341+ # Cache the log result and delete the file on disk
342+ self._get_log(False)
343
344 def thisFailsStrictLockCheck(self):
345 """It is known that this test would fail with -Dstrict_locks.
346@@ -1587,7 +1540,8 @@
347 else:
348 addSkip(self, reason)
349
350- def _do_known_failure(self, result):
351+ @staticmethod
352+ def _do_known_failure(self, result, e):
353 err = sys.exc_info()
354 addExpectedFailure = getattr(result, 'addExpectedFailure', None)
355 if addExpectedFailure is not None:
356@@ -1595,6 +1549,7 @@
357 else:
358 result.addSuccess(self)
359
360+ @staticmethod
361 def _do_not_applicable(self, result, e):
362 if not e.args:
363 reason = 'No reason given'
364@@ -1606,120 +1561,15 @@
365 else:
366 self._do_skip(result, reason)
367
368- def _do_unsupported_or_skip(self, result, reason):
369+ @staticmethod
370+ def _do_unsupported_or_skip(self, result, e):
371+ reason = e.args[0]
372 addNotSupported = getattr(result, 'addNotSupported', None)
373 if addNotSupported is not None:
374 result.addNotSupported(self, reason)
375 else:
376 self._do_skip(result, reason)
377
378- def run(self, result=None):
379- if result is None: result = self.defaultTestResult()
380- result.startTest(self)
381- try:
382- self._run(result)
383- return result
384- finally:
385- result.stopTest(self)
386-
387- def _run(self, result):
388- for feature in getattr(self, '_test_needs_features', []):
389- if not feature.available():
390- return self._do_unsupported_or_skip(result, feature)
391- try:
392- absent_attr = object()
393- # Python 2.5
394- method_name = getattr(self, '_testMethodName', absent_attr)
395- if method_name is absent_attr:
396- # Python 2.4
397- method_name = getattr(self, '_TestCase__testMethodName')
398- testMethod = getattr(self, method_name)
399- try:
400- try:
401- self.setUp()
402- if not self._bzr_test_setUp_run:
403- self.fail(
404- "test setUp did not invoke "
405- "bzrlib.tests.TestCase's setUp")
406- except KeyboardInterrupt:
407- self._runCleanups()
408- raise
409- except KnownFailure:
410- self._do_known_failure(result)
411- self.tearDown()
412- return
413- except TestNotApplicable, e:
414- self._do_not_applicable(result, e)
415- self.tearDown()
416- return
417- except TestSkipped, e:
418- self._do_skip(result, e.args[0])
419- self.tearDown()
420- return result
421- except UnavailableFeature, e:
422- self._do_unsupported_or_skip(result, e.args[0])
423- self.tearDown()
424- return
425- except:
426- result.addError(self, sys.exc_info())
427- self._runCleanups()
428- return result
429-
430- ok = False
431- try:
432- testMethod()
433- ok = True
434- except KnownFailure:
435- self._do_known_failure(result)
436- except self.failureException:
437- result.addFailure(self, sys.exc_info())
438- except TestNotApplicable, e:
439- self._do_not_applicable(result, e)
440- except TestSkipped, e:
441- if not e.args:
442- reason = "No reason given."
443- else:
444- reason = e.args[0]
445- self._do_skip(result, reason)
446- except UnavailableFeature, e:
447- self._do_unsupported_or_skip(result, e.args[0])
448- except KeyboardInterrupt:
449- self._runCleanups()
450- raise
451- except:
452- result.addError(self, sys.exc_info())
453-
454- try:
455- self.tearDown()
456- if not self._bzr_test_tearDown_run:
457- self.fail(
458- "test tearDown did not invoke "
459- "bzrlib.tests.TestCase's tearDown")
460- except KeyboardInterrupt:
461- self._runCleanups()
462- raise
463- except:
464- result.addError(self, sys.exc_info())
465- self._runCleanups()
466- ok = False
467- if ok: result.addSuccess(self)
468- return result
469- except KeyboardInterrupt:
470- self._runCleanups()
471- raise
472- finally:
473- saved_attrs = {}
474- for attr_name in self.attrs_to_keep:
475- if attr_name in self.__dict__:
476- saved_attrs[attr_name] = self.__dict__[attr_name]
477- self.__dict__ = saved_attrs
478-
479- def tearDown(self):
480- self._runCleanups()
481- self._log_contents = ''
482- self._bzr_test_tearDown_run = True
483- unittest.TestCase.tearDown(self)
484-
485 def time(self, callable, *args, **kwargs):
486 """Run callable and accrue the time it takes to the benchmark time.
487
488@@ -1728,6 +1578,8 @@
489 self._benchcalls.
490 """
491 if self._benchtime is None:
492+ self.addDetail('benchtime', content.Content(content.ContentType(
493+ "text", "plain"), lambda:[str(self._benchtime)]))
494 self._benchtime = 0
495 start = time.time()
496 try:
497@@ -1742,25 +1594,14 @@
498 finally:
499 self._benchtime += time.time() - start
500
501- def _runCleanups(self):
502- """Run registered cleanup functions.
503-
504- This should only be called from TestCase.tearDown.
505- """
506- # TODO: Perhaps this should keep running cleanups even if
507- # one of them fails?
508-
509- # Actually pop the cleanups from the list so tearDown running
510- # twice is safe (this happens for skipped tests).
511- while self._cleanups:
512- cleanup, args, kwargs = self._cleanups.pop()
513- cleanup(*args, **kwargs)
514-
515 def log(self, *args):
516 mutter(*args)
517
518 def _get_log(self, keep_log_file=False):
519- """Get the log from bzrlib.trace calls from this test.
520+ """Internal helper to get the log from bzrlib.trace for this test.
521+
522+ Please use self.getDetails, or self.get_log to access this in test case
523+ code.
524
525 :param keep_log_file: When True, if the log is still a file on disk
526 leave it as a file on disk. When False, if the log is still a file
527@@ -1768,21 +1609,33 @@
528 self._log_contents.
529 :return: A string containing the log.
530 """
531- # flush the log file, to get all content
532+ if self._log_contents is not None:
533+ try:
534+ self._log_contents.decode('utf8')
535+ except UnicodeDecodeError:
536+ unicodestr = self._log_contents.decode('utf8', 'replace')
537+ self._log_contents = unicodestr.encode('utf8')
538+ return self._log_contents
539 import bzrlib.trace
540 if bzrlib.trace._trace_file:
541+ # flush the log file, to get all content
542 bzrlib.trace._trace_file.flush()
543- if self._log_contents:
544- # XXX: this can hardly contain the content flushed above --vila
545- # 20080128
546- return self._log_contents
547 if self._log_file_name is not None:
548 logfile = open(self._log_file_name)
549 try:
550 log_contents = logfile.read()
551 finally:
552 logfile.close()
553+ try:
554+ log_contents.decode('utf8')
555+ except UnicodeDecodeError:
556+ unicodestr = log_contents.decode('utf8', 'replace')
557+ log_contents = unicodestr.encode('utf8')
558 if not keep_log_file:
559+ self._log_file.close()
560+ self._log_file = None
561+ # Permit multiple calls to get_log until we clean it up in
562+ # finishLogFile
563 self._log_contents = log_contents
564 try:
565 os.remove(self._log_file_name)
566@@ -1792,9 +1645,17 @@
567 ' %r\n' % self._log_file_name))
568 else:
569 raise
570+ self._log_file_name = None
571 return log_contents
572 else:
573- return "DELETED log file to reduce memory footprint"
574+ return "No log file content and no log file name."
575+
576+ def get_log(self):
577+ """Get a unicode string containing the log from bzrlib.trace.
578+
579+ Undecodable characters are replaced.
580+ """
581+ return u"".join(self.getDetails()['log'].iter_text())
582
583 def requireFeature(self, feature):
584 """This test requires a specific feature is available.
585@@ -3219,7 +3080,7 @@
586 if self.randomised:
587 return iter(self._tests)
588 self.randomised = True
589- self.stream.writeln("Randomizing test order using seed %s\n" %
590+ self.stream.write("Randomizing test order using seed %s\n\n" %
591 (self.actual_seed()))
592 # Initialise the random number generator.
593 random.seed(self.actual_seed())
594@@ -3310,7 +3171,7 @@
595 sys.stdin.close()
596 sys.stdin = None
597 stream = os.fdopen(c2pwrite, 'wb', 1)
598- subunit_result = BzrAutoTimingTestResultDecorator(
599+ subunit_result = AutoTimingTestResultDecorator(
600 TestProtocolClient(stream))
601 process_suite.run(subunit_result)
602 finally:
603@@ -3408,56 +3269,10 @@
604
605 def addFailure(self, test, err):
606 self.result.addFailure(test, err)
607-
608-
609-class BZRTransformingResult(ForwardingResult):
610-
611- def addError(self, test, err):
612- feature = self._error_looks_like('UnavailableFeature: ', err)
613- if feature is not None:
614- self.result.addNotSupported(test, feature)
615- else:
616- self.result.addError(test, err)
617-
618- def addFailure(self, test, err):
619- known = self._error_looks_like('KnownFailure: ', err)
620- if known is not None:
621- self.result.addExpectedFailure(test,
622- [KnownFailure, KnownFailure(known), None])
623- else:
624- self.result.addFailure(test, err)
625-
626- def _error_looks_like(self, prefix, err):
627- """Deserialize exception and returns the stringify value."""
628- import subunit
629- value = None
630- typ, exc, _ = err
631- if isinstance(exc, subunit.RemoteException):
632- # stringify the exception gives access to the remote traceback
633- # We search the last line for 'prefix'
634- lines = str(exc).split('\n')
635- while lines and not lines[-1]:
636- lines.pop(-1)
637- if lines:
638- if lines[-1].startswith(prefix):
639- value = lines[-1][len(prefix):]
640- return value
641-
642-
643-try:
644- from subunit.test_results import AutoTimingTestResultDecorator
645- # Expected failure should be seen as a success not a failure Once subunit
646- # provide native support for that, BZRTransformingResult and this class
647- # will become useless.
648- class BzrAutoTimingTestResultDecorator(AutoTimingTestResultDecorator):
649-
650- def addExpectedFailure(self, test, err):
651- self._before_event()
652- return self._call_maybe("addExpectedFailure", self._degrade_skip,
653- test, err)
654-except ImportError:
655- # Let's just define a no-op decorator
656- BzrAutoTimingTestResultDecorator = lambda x:x
657+ForwardingResult = testtools.ExtendedToOriginalDecorator
658+
659+
660+from subunit.test_results import AutoTimingTestResultDecorator
661
662
663 class ProfileResult(ForwardingResult):
664@@ -4478,7 +4293,7 @@
665 from subunit import TestProtocolClient
666 class SubUnitBzrRunner(TextTestRunner):
667 def run(self, test):
668- result = BzrAutoTimingTestResultDecorator(
669+ result = AutoTimingTestResultDecorator(
670 TestProtocolClient(self.stream))
671 test.run(result)
672 return result
673
674=== modified file 'bzrlib/tests/blackbox/test_debug.py'
675--- bzrlib/tests/blackbox/test_debug.py 2009-03-23 14:59:43 +0000
676+++ bzrlib/tests/blackbox/test_debug.py 2009-12-17 09:43:14 +0000
677@@ -38,5 +38,4 @@
678 def test_dash_dlock(self):
679 # With -Dlock, locking and unlocking is recorded into the log
680 self.run_bzr("-Dlock init foo")
681- trace_messages = self._get_log(keep_log_file=True)
682- self.assertContainsRe(trace_messages, "lock_write")
683+ self.assertContainsRe(self.get_log(), "lock_write")
684
685=== modified file 'bzrlib/tests/blackbox/test_exceptions.py'
686--- bzrlib/tests/blackbox/test_exceptions.py 2009-08-20 06:25:02 +0000
687+++ bzrlib/tests/blackbox/test_exceptions.py 2009-12-17 09:43:14 +0000
688@@ -55,8 +55,7 @@
689 os.mkdir('foo')
690 bzrdir.BzrDirFormat5().initialize('foo')
691 out, err = self.run_bzr("status foo")
692- self.assertContainsRe(self._get_log(keep_log_file=True),
693- "bzr upgrade")
694+ self.assertContainsRe(self.get_log(), "bzr upgrade")
695 finally:
696 repository._deprecation_warning_done = True
697
698
699=== modified file 'bzrlib/tests/per_pack_repository.py'
700--- bzrlib/tests/per_pack_repository.py 2009-09-07 03:35:06 +0000
701+++ bzrlib/tests/per_pack_repository.py 2009-12-17 09:43:14 +0000
702@@ -687,9 +687,9 @@
703 # abort_write_group will not raise an error
704 self.assertEqual(None, repo.abort_write_group(suppress_errors=True))
705 # But it does log an error
706- log_file = self._get_log(keep_log_file=True)
707- self.assertContainsRe(log_file, 'abort_write_group failed')
708- self.assertContainsRe(log_file, r'INFO bzr: ERROR \(ignored\):')
709+ log = self.get_log()
710+ self.assertContainsRe(log, 'abort_write_group failed')
711+ self.assertContainsRe(log, r'INFO bzr: ERROR \(ignored\):')
712 if token is not None:
713 repo.leave_lock_in_place()
714
715
716=== modified file 'bzrlib/tests/per_repository/test_check.py'
717--- bzrlib/tests/per_repository/test_check.py 2009-08-30 23:51:10 +0000
718+++ bzrlib/tests/per_repository/test_check.py 2009-12-17 09:43:14 +0000
719@@ -46,8 +46,7 @@
720 revid2 = tree.commit('2')
721 check_object = tree.branch.repository.check([revid1, revid2])
722 check_object.report_results(verbose=True)
723- log = self._get_log(keep_log_file=True)
724- self.assertContainsRe(log, "0 unreferenced text versions")
725+ self.assertContainsRe(self.get_log(), "0 unreferenced text versions")
726
727
728 class TestFindInconsistentRevisionParents(TestCaseWithBrokenRevisionIndex):
729@@ -90,13 +89,11 @@
730 # contents of it!
731 check_object = repo.check(['ignored'])
732 check_object.report_results(verbose=False)
733- log = self._get_log(keep_log_file=True)
734- self.assertContainsRe(
735- log, '1 revisions have incorrect parents in the revision index')
736+ self.assertContainsRe(self.get_log(),
737+ '1 revisions have incorrect parents in the revision index')
738 check_object.report_results(verbose=True)
739- log = self._get_log(keep_log_file=True)
740 self.assertContainsRe(
741- log,
742+ self.get_log(),
743 "revision-id has wrong parents in index: "
744 r"\('incorrect-parent',\) should be \(\)")
745
746@@ -147,5 +144,5 @@
747 rev_id = builder.commit('first post')
748 result = repo.check(None, check_repo=True)
749 result.report_results(True)
750- log = self._get_log(keep_log_file=True)
751+ log = self.get_log()
752 self.assertFalse('Missing' in log, "Something was missing in %r" % log)
753
754=== modified file 'bzrlib/tests/per_repository/test_check_reconcile.py'
755--- bzrlib/tests/per_repository/test_check_reconcile.py 2009-03-23 14:59:43 +0000
756+++ bzrlib/tests/per_repository/test_check_reconcile.py 2009-12-17 09:43:14 +0000
757@@ -198,10 +198,9 @@
758 repo, scenario = self.prepare_test_repository()
759 check_result = repo.check()
760 check_result.report_results(verbose=True)
761+ log = self.get_log()
762 for pattern in scenario.check_regexes(repo):
763- self.assertContainsRe(
764- self._get_log(keep_log_file=True),
765- pattern)
766+ self.assertContainsRe(log, pattern)
767
768 def test_find_text_key_references(self):
769 """Test that find_text_key_references finds erroneous references."""
770
771=== modified file 'bzrlib/tests/per_repository_reference/test_check.py'
772--- bzrlib/tests/per_repository_reference/test_check.py 2009-08-11 05:26:57 +0000
773+++ bzrlib/tests/per_repository_reference/test_check.py 2009-12-17 09:43:14 +0000
774@@ -39,5 +39,4 @@
775 check_result = referring.branch.repository.check(
776 referring.branch.repository.all_revision_ids())
777 check_result.report_results(verbose=False)
778- log = self._get_log(keep_log_file=True)
779- self.assertFalse("inconsistent parents" in log)
780+ self.assertFalse("inconsistent parents" in self.get_log())
781
782=== modified file 'bzrlib/tests/test_cleanup.py'
783--- bzrlib/tests/test_cleanup.py 2009-10-26 06:23:14 +0000
784+++ bzrlib/tests/test_cleanup.py 2009-12-17 09:43:14 +0000
785@@ -39,8 +39,7 @@
786 self.call_log.append('no_op_cleanup')
787
788 def assertLogContains(self, regex):
789- log = self._get_log(keep_log_file=True)
790- self.assertContainsRe(log, regex, re.DOTALL)
791+ self.assertContainsRe(self.get_log(), regex, re.DOTALL)
792
793 def failing_cleanup(self):
794 self.call_log.append('failing_cleanup')
795@@ -184,8 +183,7 @@
796 self.assertRaises(ErrorA, _do_with_cleanups, cleanups,
797 self.trivial_func)
798 self.assertLogContains('Cleanup failed:.*ErrorB')
799- log = self._get_log(keep_log_file=True)
800- self.assertFalse('ErrorA' in log)
801+ self.assertFalse('ErrorA' in self.get_log())
802
803 def make_two_failing_cleanup_funcs(self):
804 def raise_a():
805
806=== modified file 'bzrlib/tests/test_config.py'
807--- bzrlib/tests/test_config.py 2009-10-31 01:43:48 +0000
808+++ bzrlib/tests/test_config.py 2009-12-17 09:43:14 +0000
809@@ -1600,7 +1600,7 @@
810 self.assertEquals(entered_password,
811 conf.get_password('ssh', 'bar.org', user='jim'))
812 self.assertContainsRe(
813- self._get_log(keep_log_file=True),
814+ self.get_log(),
815 'password ignored in section \[ssh with password\]')
816
817 def test_ssh_without_password_doesnt_emit_warning(self):
818@@ -1625,7 +1625,7 @@
819 # No warning shoud be emitted since there is no password. We are only
820 # providing "user".
821 self.assertNotContainsRe(
822- self._get_log(keep_log_file=True),
823+ self.get_log(),
824 'password ignored in section \[ssh with password\]')
825
826 def test_uses_fallback_stores(self):
827
828=== modified file 'bzrlib/tests/test_http_response.py'
829--- bzrlib/tests/test_http_response.py 2009-03-23 14:59:43 +0000
830+++ bzrlib/tests/test_http_response.py 2009-12-17 09:43:14 +0000
831@@ -96,8 +96,7 @@
832 # Override the thresold to force the warning emission
833 conn._range_warning_thresold = 6 # There are 7 bytes pending
834 conn.cleanup_pipe()
835- self.assertContainsRe(self._get_log(keep_log_file=True),
836- 'Got a 200 response when asking')
837+ self.assertContainsRe(self.get_log(), 'Got a 200 response when asking')
838
839
840 class TestRangeFileMixin(object):
841
842=== modified file 'bzrlib/tests/test_merge.py'
843--- bzrlib/tests/test_merge.py 2009-12-10 17:16:19 +0000
844+++ bzrlib/tests/test_merge.py 2009-12-17 09:43:14 +0000
845@@ -152,13 +152,12 @@
846 log = StringIO()
847 merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),
848 this_tree=tree_b, ignore_zero=True)
849- log = self._get_log(keep_log_file=True)
850- self.failUnless('All changes applied successfully.\n' not in log)
851+ self.failUnless('All changes applied successfully.\n' not in
852+ self.get_log())
853 tree_b.revert()
854 merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),
855 this_tree=tree_b, ignore_zero=False)
856- log = self._get_log(keep_log_file=True)
857- self.failUnless('All changes applied successfully.\n' in log)
858+ self.failUnless('All changes applied successfully.\n' in self.get_log())
859
860 def test_merge_inner_conflicts(self):
861 tree_a = self.make_branch_and_tree('a')
862
863=== modified file 'bzrlib/tests/test_remote.py'
864--- bzrlib/tests/test_remote.py 2009-12-03 20:34:03 +0000
865+++ bzrlib/tests/test_remote.py 2009-12-17 09:43:14 +0000
866@@ -2893,7 +2893,7 @@
867 # In addition to re-raising ErrorFromSmartServer, some debug info has
868 # been muttered to the log file for developer to look at.
869 self.assertContainsRe(
870- self._get_log(keep_log_file=True),
871+ self.get_log(),
872 "Missing key 'branch' in context")
873
874 def test_path_missing(self):
875@@ -2907,8 +2907,7 @@
876 self.assertEqual(server_error, translated_error)
877 # In addition to re-raising ErrorFromSmartServer, some debug info has
878 # been muttered to the log file for developer to look at.
879- self.assertContainsRe(
880- self._get_log(keep_log_file=True), "Missing key 'path' in context")
881+ self.assertContainsRe(self.get_log(), "Missing key 'path' in context")
882
883
884 class TestStacking(tests.TestCaseWithTransport):
885
886=== modified file 'bzrlib/tests/test_selftest.py'
887--- bzrlib/tests/test_selftest.py 2009-12-08 21:46:07 +0000
888+++ bzrlib/tests/test_selftest.py 2009-12-17 09:43:14 +0000
889@@ -17,6 +17,7 @@
890 """Tests for the test framework."""
891
892 from cStringIO import StringIO
893+from doctest import ELLIPSIS
894 import os
895 import signal
896 import sys
897@@ -24,6 +25,14 @@
898 import unittest
899 import warnings
900
901+from testtools import MultiTestResult
902+from testtools.content_type import ContentType
903+from testtools.matchers import (
904+ DocTestMatches,
905+ Equals,
906+ )
907+import testtools.tests.helpers
908+
909 import bzrlib
910 from bzrlib import (
911 branchbuilder,
912@@ -78,14 +87,19 @@
913 TestUtil._load_module_by_name,
914 'bzrlib.no-name-yet')
915
916+
917 class MetaTestLog(tests.TestCase):
918
919 def test_logging(self):
920 """Test logs are captured when a test fails."""
921 self.log('a test message')
922- self._log_file.flush()
923- self.assertContainsRe(self._get_log(keep_log_file=True),
924- 'a test message\n')
925+ details = self.getDetails()
926+ log = details['log']
927+ self.assertThat(log.content_type, Equals(ContentType(
928+ "text", "plain", {"charset": "utf8"})))
929+ self.assertThat(u"".join(log.iter_text()), Equals(self.get_log()))
930+ self.assertThat(self.get_log(),
931+ DocTestMatches(u"...a test message\n", ELLIPSIS))
932
933
934 class TestUnicodeFilename(tests.TestCase):
935@@ -687,7 +701,7 @@
936
937 def test_profiles_tests(self):
938 self.requireFeature(test_lsprof.LSProfFeature)
939- terminal = unittest.TestResult()
940+ terminal = testtools.tests.helpers.ExtendedTestResult()
941 result = tests.ProfileResult(terminal)
942 class Sample(tests.TestCase):
943 def a(self):
944@@ -695,11 +709,11 @@
945 def sample_function(self):
946 pass
947 test = Sample("a")
948- test.attrs_to_keep = test.attrs_to_keep + ('_benchcalls',)
949 test.run(result)
950- self.assertLength(1, test._benchcalls)
951+ case = terminal._events[0][1]
952+ self.assertLength(1, case._benchcalls)
953 # We must be able to unpack it as the test reporting code wants
954- (_, _, _), stats = test._benchcalls[0]
955+ (_, _, _), stats = case._benchcalls[0]
956 self.assertTrue(callable(stats.pprint))
957
958
959@@ -710,8 +724,10 @@
960 descriptions=0,
961 verbosity=1,
962 )
963- test_case.run(result)
964- timed_string = result._testTimeString(test_case)
965+ capture = testtools.tests.helpers.ExtendedTestResult()
966+ test_case.run(MultiTestResult(result, capture))
967+ run_case = capture._events[0][1]
968+ timed_string = result._testTimeString(run_case)
969 self.assertContainsRe(timed_string, expected_re)
970
971 def test_test_reporting(self):
972@@ -832,8 +848,8 @@
973 def stopTestRun(self): pass
974 def startTests(self): pass
975 def report_test_start(self, test): pass
976- def report_known_failure(self, test, err):
977- self._call = test, err
978+ def report_known_failure(self, test, err=None, details=None):
979+ self._call = test, 'known failure'
980 result = InstrumentedTestResult(None, None, None, None)
981 class Test(tests.TestCase):
982 def test_function(self):
983@@ -842,9 +858,8 @@
984 test.run(result)
985 # it should invoke 'report_known_failure'.
986 self.assertEqual(2, len(result._call))
987- self.assertEqual(test, result._call[0])
988- self.assertEqual(tests.KnownFailure, result._call[1][0])
989- self.assertIsInstance(result._call[1][1], tests.KnownFailure)
990+ self.assertEqual(test.id(), result._call[0].id())
991+ self.assertEqual('known failure', result._call[1])
992 # we dont introspec the traceback, if the rest is ok, it would be
993 # exceptional for it not to be.
994 # it should update the known_failure_count on the object.
995@@ -944,7 +959,7 @@
996 test.run(result)
997 # it should invoke 'addNotSupported'.
998 self.assertEqual(2, len(result._call))
999- self.assertEqual(test, result._call[0])
1000+ self.assertEqual(test.id(), result._call[0].id())
1001 self.assertEqual(feature, result._call[1])
1002 # and not count as an error
1003 self.assertEqual(0, result.error_count)
1004@@ -1028,25 +1043,25 @@
1005 # the final output when real failures occur.
1006 class Test(tests.TestCase):
1007 def known_failure_test(self):
1008- raise tests.KnownFailure('failed')
1009+ self.expectFailure('failed', self.assertTrue, False)
1010 test = unittest.TestSuite()
1011 test.addTest(Test("known_failure_test"))
1012 def failing_test():
1013- raise AssertionError('foo')
1014+ self.fail('foo')
1015 test.addTest(unittest.FunctionTestCase(failing_test))
1016 stream = StringIO()
1017 runner = tests.TextTestRunner(stream=stream)
1018 result = self.run_test_runner(runner, test)
1019 lines = stream.getvalue().splitlines()
1020 self.assertContainsRe(stream.getvalue(),
1021- '(?sm)^testing.*$'
1022+ '(?sm)^bzr selftest.*$'
1023 '.*'
1024 '^======================================================================\n'
1025 '^FAIL: unittest.FunctionTestCase \\(failing_test\\)\n'
1026 '^----------------------------------------------------------------------\n'
1027 'Traceback \\(most recent call last\\):\n'
1028 ' .*' # File .*, line .*, in failing_test' - but maybe not from .pyc
1029- ' raise AssertionError\\(\'foo\'\\)\n'
1030+ ' self.fail\\(\'foo\'\\)\n'
1031 '.*'
1032 '^----------------------------------------------------------------------\n'
1033 '.*'
1034@@ -1058,7 +1073,7 @@
1035 # the final output.
1036 class Test(tests.TestCase):
1037 def known_failure_test(self):
1038- raise tests.KnownFailure('failed')
1039+ self.expectFailure('failed', self.assertTrue, False)
1040 test = Test("known_failure_test")
1041 stream = StringIO()
1042 runner = tests.TextTestRunner(stream=stream)
1043@@ -1209,119 +1224,6 @@
1044 self.assertContainsRe(output_string, "--date [0-9.]+")
1045 self.assertLength(1, self._get_source_tree_calls)
1046
1047- def assertLogDeleted(self, test):
1048- log = test._get_log()
1049- self.assertEqual("DELETED log file to reduce memory footprint", log)
1050- self.assertEqual('', test._log_contents)
1051- self.assertIs(None, test._log_file_name)
1052-
1053- def test_success_log_deleted(self):
1054- """Successful tests have their log deleted"""
1055-
1056- class LogTester(tests.TestCase):
1057-
1058- def test_success(self):
1059- self.log('this will be removed\n')
1060-
1061- sio = StringIO()
1062- runner = tests.TextTestRunner(stream=sio)
1063- test = LogTester('test_success')
1064- result = self.run_test_runner(runner, test)
1065-
1066- self.assertLogDeleted(test)
1067-
1068- def test_skipped_log_deleted(self):
1069- """Skipped tests have their log deleted"""
1070-
1071- class LogTester(tests.TestCase):
1072-
1073- def test_skipped(self):
1074- self.log('this will be removed\n')
1075- raise tests.TestSkipped()
1076-
1077- sio = StringIO()
1078- runner = tests.TextTestRunner(stream=sio)
1079- test = LogTester('test_skipped')
1080- result = self.run_test_runner(runner, test)
1081-
1082- self.assertLogDeleted(test)
1083-
1084- def test_not_aplicable_log_deleted(self):
1085- """Not applicable tests have their log deleted"""
1086-
1087- class LogTester(tests.TestCase):
1088-
1089- def test_not_applicable(self):
1090- self.log('this will be removed\n')
1091- raise tests.TestNotApplicable()
1092-
1093- sio = StringIO()
1094- runner = tests.TextTestRunner(stream=sio)
1095- test = LogTester('test_not_applicable')
1096- result = self.run_test_runner(runner, test)
1097-
1098- self.assertLogDeleted(test)
1099-
1100- def test_known_failure_log_deleted(self):
1101- """Know failure tests have their log deleted"""
1102-
1103- class LogTester(tests.TestCase):
1104-
1105- def test_known_failure(self):
1106- self.log('this will be removed\n')
1107- raise tests.KnownFailure()
1108-
1109- sio = StringIO()
1110- runner = tests.TextTestRunner(stream=sio)
1111- test = LogTester('test_known_failure')
1112- result = self.run_test_runner(runner, test)
1113-
1114- self.assertLogDeleted(test)
1115-
1116- def test_fail_log_kept(self):
1117- """Failed tests have their log kept"""
1118-
1119- class LogTester(tests.TestCase):
1120-
1121- def test_fail(self):
1122- self.log('this will be kept\n')
1123- self.fail('this test fails')
1124-
1125- sio = StringIO()
1126- runner = tests.TextTestRunner(stream=sio)
1127- test = LogTester('test_fail')
1128- result = self.run_test_runner(runner, test)
1129-
1130- text = sio.getvalue()
1131- self.assertContainsRe(text, 'this will be kept')
1132- self.assertContainsRe(text, 'this test fails')
1133-
1134- log = test._get_log()
1135- self.assertContainsRe(log, 'this will be kept')
1136- self.assertEqual(log, test._log_contents)
1137-
1138- def test_error_log_kept(self):
1139- """Tests with errors have their log kept"""
1140-
1141- class LogTester(tests.TestCase):
1142-
1143- def test_error(self):
1144- self.log('this will be kept\n')
1145- raise ValueError('random exception raised')
1146-
1147- sio = StringIO()
1148- runner = tests.TextTestRunner(stream=sio)
1149- test = LogTester('test_error')
1150- result = self.run_test_runner(runner, test)
1151-
1152- text = sio.getvalue()
1153- self.assertContainsRe(text, 'this will be kept')
1154- self.assertContainsRe(text, 'random exception raised')
1155-
1156- log = test._get_log()
1157- self.assertContainsRe(log, 'this will be kept')
1158- self.assertEqual(log, test._log_contents)
1159-
1160 def test_startTestRun(self):
1161 """run should call result.startTestRun()"""
1162 calls = []
1163@@ -1491,6 +1393,7 @@
1164 self.assertEqual(set(['original-state']), bzrlib.debug.debug_flags)
1165
1166 def make_test_result(self):
1167+ """Get a test result that writes to the test log file."""
1168 return tests.TextTestResult(self._log_file, descriptions=0, verbosity=1)
1169
1170 def inner_test(self):
1171@@ -1504,6 +1407,7 @@
1172 result = self.make_test_result()
1173 self.inner_test.run(result)
1174 note("outer finish")
1175+ self.addCleanup(osutils.delete_any, self._log_file_name)
1176
1177 def test_trace_nesting(self):
1178 # this tests that each test case nests its trace facility correctly.
1179@@ -1521,7 +1425,6 @@
1180 outer_test = TestTestCase("outer_child")
1181 result = self.make_test_result()
1182 outer_test.run(result)
1183- self.addCleanup(osutils.delete_any, outer_test._log_file_name)
1184 self.assertEqual(original_trace, bzrlib.trace._trace_file)
1185
1186 def method_that_times_a_bit_twice(self):
1187@@ -1642,6 +1545,8 @@
1188 """Test disabled tests behaviour with support aware results."""
1189 test = SampleTestCase('_test_pass')
1190 class DisabledFeature(object):
1191+ def __eq__(self, other):
1192+ return isinstance(other, DisabledFeature)
1193 def available(self):
1194 return False
1195 the_feature = DisabledFeature()
1196@@ -1658,10 +1563,11 @@
1197 self.calls.append(('addNotSupported', test, feature))
1198 result = InstrumentedTestResult()
1199 test.run(result)
1200+ case = result.calls[0][1]
1201 self.assertEqual([
1202- ('startTest', test),
1203- ('addNotSupported', test, the_feature),
1204- ('stopTest', test),
1205+ ('startTest', case),
1206+ ('addNotSupported', case, the_feature),
1207+ ('stopTest', case),
1208 ],
1209 result.calls)
1210
1211@@ -2434,28 +2340,6 @@
1212 self.assertEqual('bzr: interrupted\n', result[1])
1213
1214
1215-class TestKnownFailure(tests.TestCase):
1216-
1217- def test_known_failure(self):
1218- """Check that KnownFailure is defined appropriately."""
1219- # a KnownFailure is an assertion error for compatability with unaware
1220- # runners.
1221- self.assertIsInstance(tests.KnownFailure(""), AssertionError)
1222-
1223- def test_expect_failure(self):
1224- try:
1225- self.expectFailure("Doomed to failure", self.assertTrue, False)
1226- except tests.KnownFailure, e:
1227- self.assertEqual('Doomed to failure', e.args[0])
1228- try:
1229- self.expectFailure("Doomed to failure", self.assertTrue, True)
1230- except AssertionError, e:
1231- self.assertEqual('Unexpected success. Should have failed:'
1232- ' Doomed to failure', e.args[0])
1233- else:
1234- self.fail('Assertion not raised')
1235-
1236-
1237 class TestFeature(tests.TestCase):
1238
1239 def test_caching(self):
1240@@ -2697,8 +2581,7 @@
1241 # the test framework
1242 self.assertEquals('always fails', str(e))
1243 # check that there's no traceback in the test log
1244- self.assertNotContainsRe(self._get_log(keep_log_file=True),
1245- r'Traceback')
1246+ self.assertNotContainsRe(self.get_log(), r'Traceback')
1247
1248 def test_run_bzr_user_error_caught(self):
1249 # Running bzr in blackbox mode, normal/expected/user errors should be
1250@@ -2957,14 +2840,12 @@
1251 def test_load_tests(self):
1252 test_list = ['bzrlib.tests.test_sampler.DemoTest.test_nothing']
1253 loader = self._create_loader(test_list)
1254-
1255 suite = loader.loadTestsFromModuleName('bzrlib.tests.test_sampler')
1256 self.assertEquals(test_list, _test_ids(suite))
1257
1258 def test_exclude_tests(self):
1259 test_list = ['bogus']
1260 loader = self._create_loader(test_list)
1261-
1262 suite = loader.loadTestsFromModuleName('bzrlib.tests.test_sampler')
1263 self.assertEquals([], _test_ids(suite))
1264
1265@@ -3015,8 +2896,8 @@
1266 tpr.register('bar', 'bbb.aaa.rrr')
1267 tpr.register('bar', 'bBB.aAA.rRR')
1268 self.assertEquals('bbb.aaa.rrr', tpr.get('bar'))
1269- self.assertContainsRe(self._get_log(keep_log_file=True),
1270- r'.*bar.*bbb.aaa.rrr.*bBB.aAA.rRR')
1271+ self.assertThat(self.get_log(),
1272+ DocTestMatches("...bar...bbb.aaa.rrr...BB.aAA.rRR", ELLIPSIS))
1273
1274 def test_get_unknown_prefix(self):
1275 tpr = self._get_registry()
1276
1277=== modified file 'bzrlib/tests/test_trace.py'
1278--- bzrlib/tests/test_trace.py 2009-11-08 19:07:00 +0000
1279+++ bzrlib/tests/test_trace.py 2009-12-17 09:43:14 +0000
1280@@ -144,21 +144,21 @@
1281 def test_trace_unicode(self):
1282 """Write Unicode to trace log"""
1283 self.log(u'the unicode character for benzene is \N{BENZENE RING}')
1284- self.assertContainsRe(self._get_log(keep_log_file=True),
1285- "the unicode character for benzene is")
1286+ log = self.get_log()
1287+ self.assertContainsRe(log, "the unicode character for benzene is")
1288
1289 def test_trace_argument_unicode(self):
1290 """Write a Unicode argument to the trace log"""
1291 mutter(u'the unicode character for benzene is %s', u'\N{BENZENE RING}')
1292- self.assertContainsRe(self._get_log(keep_log_file=True),
1293- 'the unicode character')
1294+ log = self.get_log()
1295+ self.assertContainsRe(log, 'the unicode character')
1296
1297 def test_trace_argument_utf8(self):
1298 """Write a Unicode argument to the trace log"""
1299 mutter(u'the unicode character for benzene is %s',
1300 u'\N{BENZENE RING}'.encode('utf-8'))
1301- self.assertContainsRe(self._get_log(keep_log_file=True),
1302- 'the unicode character')
1303+ log = self.get_log()
1304+ self.assertContainsRe(log, 'the unicode character')
1305
1306 def test_report_broken_pipe(self):
1307 try:
1308@@ -177,7 +177,7 @@
1309 def test_mutter_callsite_1(self):
1310 """mutter_callsite can capture 1 level of stack frame."""
1311 mutter_callsite(1, "foo %s", "a string")
1312- log = self._get_log(keep_log_file=True)
1313+ log = self.get_log()
1314 # begin with the message
1315 self.assertLogStartsWith(log, 'foo a string\nCalled from:\n')
1316 # should show two frame: this frame and the one above
1317@@ -189,7 +189,7 @@
1318 def test_mutter_callsite_2(self):
1319 """mutter_callsite can capture 2 levels of stack frame."""
1320 mutter_callsite(2, "foo %s", "a string")
1321- log = self._get_log(keep_log_file=True)
1322+ log = self.get_log()
1323 # begin with the message
1324 self.assertLogStartsWith(log, 'foo a string\nCalled from:\n')
1325 # should show two frame: this frame and the one above
1326@@ -201,13 +201,19 @@
1327 def test_mutter_never_fails(self):
1328 # Even if the decode/encode stage fails, mutter should not
1329 # raise an exception
1330+ # This test checks that mutter doesn't fail; the current behaviour
1331+ # is that it doesn't fail *and writes non-utf8*.
1332 mutter(u'Writing a greek mu (\xb5) works in a unicode string')
1333 mutter('But fails in an ascii string \xb5')
1334 mutter('and in an ascii argument: %s', '\xb5')
1335- log = self._get_log(keep_log_file=True)
1336+ log = self.get_log()
1337 self.assertContainsRe(log, 'Writing a greek mu')
1338 self.assertContainsRe(log, "But fails in an ascii string")
1339- self.assertContainsRe(log, u"ascii argument: \xb5")
1340+ # However, the log content object does unicode replacement on reading
1341+ # to let it get unicode back where good data has been written. So we
1342+ # have to do a replaceent here as well.
1343+ self.assertContainsRe(log, "ascii argument: \xb5".decode('utf8',
1344+ 'replace'))
1345
1346 def test_push_log_file(self):
1347 """Can push and pop log file, and this catches mutter messages.
1348
1349=== modified file 'bzrlib/tests/test_transport_log.py'
1350--- bzrlib/tests/test_transport_log.py 2009-07-01 07:04:47 +0000
1351+++ bzrlib/tests/test_transport_log.py 2009-12-17 09:43:14 +0000
1352@@ -36,10 +36,9 @@
1353 # operations such as mkdir are logged
1354 mutter('where are you?')
1355 logging_transport.mkdir('subdir')
1356- self.assertContainsRe(self._get_log(True),
1357- r'mkdir memory\+\d+://.*subdir')
1358- self.assertContainsRe(self._get_log(True),
1359- ' --> None')
1360+ log = self.get_log()
1361+ self.assertContainsRe(log, r'mkdir memory\+\d+://.*subdir')
1362+ self.assertContainsRe(log, ' --> None')
1363 # they have the expected effect
1364 self.assertTrue(logging_transport.has('subdir'))
1365 # and they operate on the underlying transport