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
=== modified file 'NEWS'
--- NEWS 2009-12-16 20:01:49 +0000
+++ NEWS 2009-12-17 09:43:14 +0000
@@ -38,9 +38,21 @@
38Internals38Internals
39*********39*********
4040
41* New helper osutils.UnicodeOrBytesToBytesWriter which encodes unicode
42 objects but passes str objects straight through. This is used for
43 selftest but may be useful for diff and other operations that generate
44 mixed output. (Robert Collins)
45
41Testing46Testing
42*******47*******
4348
49* ``bzrlib.tests.TestCase`` now subclasses ``testtools.testcase.TestCase``.
50 This permits features in testtools such as getUniqueInteger and
51 getUniqueString to be used. Because of this, testtools version 0.9.2 or
52 newer is now a dependency to run bzr selftest. Running with versions of
53 testtools less than 0.9.2 will cause bzr to error while loading the test
54 suite. (Robert Collins)
55
44bzr 2.0.4 (not released yet)56bzr 2.0.4 (not released yet)
45############################57############################
4658
4759
=== modified file 'bzrlib/osutils.py'
--- bzrlib/osutils.py 2009-12-16 10:54:55 +0000
+++ bzrlib/osutils.py 2009-12-17 09:43:14 +0000
@@ -2087,3 +2087,18 @@
2087 if use_cache:2087 if use_cache:
2088 _cached_concurrency = concurrency2088 _cached_concurrency = concurrency
2089 return concurrency2089 return concurrency
2090
2091
2092class UnicodeOrBytesToBytesWriter(codecs.StreamWriter):
2093 """A stream writer that doesn't decode str arguments."""
2094
2095 def __init__(self, codec, stream, errors='strict'):
2096 codecs.StreamWriter.__init__(self, stream, errors)
2097 self.encode = codec.encode
2098
2099 def write(self, object):
2100 if type(object) is str:
2101 self.stream.write(object)
2102 else:
2103 data, _ = self.encode(object, self.errors)
2104 self.stream.write(data)
20902105
=== modified file 'bzrlib/tests/TestUtil.py'
--- bzrlib/tests/TestUtil.py 2009-03-23 14:59:43 +0000
+++ bzrlib/tests/TestUtil.py 2009-12-17 09:43:14 +0000
@@ -72,6 +72,18 @@
72 visitor.visitSuite(self)72 visitor.visitSuite(self)
73 visitTests(self, visitor)73 visitTests(self, visitor)
7474
75 def run(self, result):
76 """Run the tests in the suite, discarding references after running."""
77 tests = list(self)
78 tests.reverse()
79 self._tests = []
80 while tests:
81 if result.shouldStop:
82 self._tests = reversed(tests)
83 break
84 tests.pop().run(result)
85 return result
86
7587
76class TestLoader(unittest.TestLoader):88class TestLoader(unittest.TestLoader):
77 """Custom TestLoader to extend the stock python one."""89 """Custom TestLoader to extend the stock python one."""
7890
=== modified file 'bzrlib/tests/__init__.py'
--- bzrlib/tests/__init__.py 2009-12-08 21:46:07 +0000
+++ bzrlib/tests/__init__.py 2009-12-17 09:43:14 +0000
@@ -46,9 +46,12 @@
46import tempfile46import tempfile
47import threading47import threading
48import time48import time
49import traceback
49import unittest50import unittest
50import warnings51import warnings
5152
53import testtools
54from testtools import content
5255
53from bzrlib import (56from bzrlib import (
54 branchbuilder,57 branchbuilder,
@@ -228,8 +231,10 @@
228 '%d non-main threads were left active in the end.\n'231 '%d non-main threads were left active in the end.\n'
229 % (TestCase._active_threads - 1))232 % (TestCase._active_threads - 1))
230233
231 def _extractBenchmarkTime(self, testCase):234 def _extractBenchmarkTime(self, testCase, details=None):
232 """Add a benchmark time for the current test case."""235 """Add a benchmark time for the current test case."""
236 if details and 'benchtime' in details:
237 return float(''.join(details['benchtime'].iter_bytes()))
233 return getattr(testCase, "_benchtime", None)238 return getattr(testCase, "_benchtime", None)
234239
235 def _elapsedTestTimeString(self):240 def _elapsedTestTimeString(self):
@@ -269,7 +274,7 @@
269 else:274 else:
270 bzr_path = sys.executable275 bzr_path = sys.executable
271 self.stream.write(276 self.stream.write(
272 'testing: %s\n' % (bzr_path,))277 'bzr selftest: %s\n' % (bzr_path,))
273 self.stream.write(278 self.stream.write(
274 ' %s\n' % (279 ' %s\n' % (
275 bzrlib.__path__[0],))280 bzrlib.__path__[0],))
@@ -320,13 +325,13 @@
320 self.stop()325 self.stop()
321 self._cleanupLogFile(test)326 self._cleanupLogFile(test)
322327
323 def addSuccess(self, test):328 def addSuccess(self, test, details=None):
324 """Tell result that test completed successfully.329 """Tell result that test completed successfully.
325330
326 Called from the TestCase run()331 Called from the TestCase run()
327 """332 """
328 if self._bench_history is not None:333 if self._bench_history is not None:
329 benchmark_time = self._extractBenchmarkTime(test)334 benchmark_time = self._extractBenchmarkTime(test, details)
330 if benchmark_time is not None:335 if benchmark_time is not None:
331 self._bench_history.write("%s %s\n" % (336 self._bench_history.write("%s %s\n" % (
332 self._formatTime(benchmark_time),337 self._formatTime(benchmark_time),
@@ -362,26 +367,6 @@
362 self.not_applicable_count += 1367 self.not_applicable_count += 1
363 self.report_not_applicable(test, reason)368 self.report_not_applicable(test, reason)
364369
365 def printErrorList(self, flavour, errors):
366 for test, err in errors:
367 self.stream.writeln(self.separator1)
368 self.stream.write("%s: " % flavour)
369 self.stream.writeln(self.getDescription(test))
370 if getattr(test, '_get_log', None) is not None:
371 log_contents = test._get_log()
372 if log_contents:
373 self.stream.write('\n')
374 self.stream.write(
375 ('vvvv[log from %s]' % test.id()).ljust(78,'-'))
376 self.stream.write('\n')
377 self.stream.write(log_contents)
378 self.stream.write('\n')
379 self.stream.write(
380 ('^^^^[log from %s]' % test.id()).ljust(78,'-'))
381 self.stream.write('\n')
382 self.stream.writeln(self.separator2)
383 self.stream.writeln("%s" % err)
384
385 def _post_mortem(self):370 def _post_mortem(self):
386 """Start a PDB post mortem session."""371 """Start a PDB post mortem session."""
387 if os.environ.get('BZR_TEST_PDB', None):372 if os.environ.get('BZR_TEST_PDB', None):
@@ -499,8 +484,7 @@
499 ))484 ))
500485
501 def report_known_failure(self, test, err):486 def report_known_failure(self, test, err):
502 ui.ui_factory.note('XFAIL: %s\n%s\n' % (487 pass
503 self._test_description(test), err[1]))
504488
505 def report_skip(self, test, reason):489 def report_skip(self, test, reason):
506 pass490 pass
@@ -604,6 +588,14 @@
604 applied left to right - the first element in the list is the 588 applied left to right - the first element in the list is the
605 innermost decorator.589 innermost decorator.
606 """590 """
591 # stream may know claim to know to write unicode strings, but in older
592 # pythons this goes sufficiently wrong that it is a bad idea. (
593 # specifically a built in file with encoding 'UTF-8' will still try
594 # to encode using ascii.
595 new_encoding = osutils.get_terminal_encoding()
596 codec = codecs.lookup(new_encoding)
597 stream = osutils.UnicodeOrBytesToBytesWriter(codec, stream)
598 stream.encoding = new_encoding
607 self.stream = unittest._WritelnDecorator(stream)599 self.stream = unittest._WritelnDecorator(stream)
608 self.descriptions = descriptions600 self.descriptions = descriptions
609 self.verbosity = verbosity601 self.verbosity = verbosity
@@ -629,14 +621,6 @@
629 for decorator in self._result_decorators:621 for decorator in self._result_decorators:
630 result = decorator(result)622 result = decorator(result)
631 result.stop_early = self.stop_on_failure623 result.stop_early = self.stop_on_failure
632 try:
633 import testtools
634 except ImportError:
635 pass
636 else:
637 if isinstance(test, testtools.ConcurrentTestSuite):
638 # We need to catch bzr specific behaviors
639 result = BZRTransformingResult(result)
640 result.startTestRun()624 result.startTestRun()
641 try:625 try:
642 test.run(result)626 test.run(result)
@@ -660,8 +644,7 @@
660 % (type(suite), suite))644 % (type(suite), suite))
661645
662646
663class TestSkipped(Exception):647TestSkipped = testtools.testcase.TestSkipped
664 """Indicates that a test was intentionally skipped, rather than failing."""
665648
666649
667class TestNotApplicable(TestSkipped):650class TestNotApplicable(TestSkipped):
@@ -673,14 +656,23 @@
673 """656 """
674657
675658
676class KnownFailure(AssertionError):659# traceback._some_str fails to format exceptions that have the default
677 """Indicates that a test failed in a precisely expected manner.660# __str__ which does an implicit ascii conversion. However, repr() on those
678661# objects works, for all that its not quite what the doctor may have ordered.
679 Such failures dont block the whole test suite from passing because they are662def _clever_some_str(value):
680 indicators of partially completed code or of future work. We have an663 try:
681 explicit error for them so that we can ensure that they are always visible:664 return str(value)
682 KnownFailures are always shown in the output of bzr selftest.665 except:
683 """666 try:
667 return repr(value).replace('\\n', '\n')
668 except:
669 return '<unprintable %s object>' % type(value).__name__
670
671traceback._some_str = _clever_some_str
672
673
674# deprecated - use self.knownFailure(), or self.expectFailure.
675KnownFailure = testtools.testcase._ExpectedFailure
684676
685677
686class UnavailableFeature(Exception):678class UnavailableFeature(Exception):
@@ -762,7 +754,7 @@
762 return NullProgressView()754 return NullProgressView()
763755
764756
765class TestCase(unittest.TestCase):757class TestCase(testtools.TestCase):
766 """Base class for bzr unit tests.758 """Base class for bzr unit tests.
767759
768 Tests that need access to disk resources should subclass760 Tests that need access to disk resources should subclass
@@ -787,24 +779,26 @@
787 _leaking_threads_tests = 0779 _leaking_threads_tests = 0
788 _first_thread_leaker_id = None780 _first_thread_leaker_id = None
789 _log_file_name = None781 _log_file_name = None
790 _log_contents = ''
791 _keep_log_file = False
792 # record lsprof data when performing benchmark calls.782 # record lsprof data when performing benchmark calls.
793 _gather_lsprof_in_benchmarks = False783 _gather_lsprof_in_benchmarks = False
794 attrs_to_keep = ('id', '_testMethodName', '_testMethodDoc',
795 '_log_contents', '_log_file_name', '_benchtime',
796 '_TestCase__testMethodName', '_TestCase__testMethodDoc',)
797784
798 def __init__(self, methodName='testMethod'):785 def __init__(self, methodName='testMethod'):
799 super(TestCase, self).__init__(methodName)786 super(TestCase, self).__init__(methodName)
800 self._cleanups = []787 self._cleanups = []
801 self._bzr_test_setUp_run = False
802 self._bzr_test_tearDown_run = False
803 self._directory_isolation = True788 self._directory_isolation = True
789 self.exception_handlers.insert(0,
790 (UnavailableFeature, self._do_unsupported_or_skip))
791 self.exception_handlers.insert(0,
792 (TestNotApplicable, self._do_not_applicable))
804793
805 def setUp(self):794 def setUp(self):
806 unittest.TestCase.setUp(self)795 super(TestCase, self).setUp()
807 self._bzr_test_setUp_run = True796 for feature in getattr(self, '_test_needs_features', []):
797 self.requireFeature(feature)
798 self._log_contents = None
799 self.addDetail("log", content.Content(content.ContentType("text",
800 "plain", {"charset": "utf8"}),
801 lambda:[self._get_log(keep_log_file=True)]))
808 self._cleanEnvironment()802 self._cleanEnvironment()
809 self._silenceUI()803 self._silenceUI()
810 self._startLogFile()804 self._startLogFile()
@@ -1289,41 +1283,6 @@
1289 m += ": " + msg1283 m += ": " + msg
1290 self.fail(m)1284 self.fail(m)
12911285
1292 def expectFailure(self, reason, assertion, *args, **kwargs):
1293 """Invoke a test, expecting it to fail for the given reason.
1294
1295 This is for assertions that ought to succeed, but currently fail.
1296 (The failure is *expected* but not *wanted*.) Please be very precise
1297 about the failure you're expecting. If a new bug is introduced,
1298 AssertionError should be raised, not KnownFailure.
1299
1300 Frequently, expectFailure should be followed by an opposite assertion.
1301 See example below.
1302
1303 Intended to be used with a callable that raises AssertionError as the
1304 'assertion' parameter. args and kwargs are passed to the 'assertion'.
1305
1306 Raises KnownFailure if the test fails. Raises AssertionError if the
1307 test succeeds.
1308
1309 example usage::
1310
1311 self.expectFailure('Math is broken', self.assertNotEqual, 54,
1312 dynamic_val)
1313 self.assertEqual(42, dynamic_val)
1314
1315 This means that a dynamic_val of 54 will cause the test to raise
1316 a KnownFailure. Once math is fixed and the expectFailure is removed,
1317 only a dynamic_val of 42 will allow the test to pass. Anything other
1318 than 54 or 42 will cause an AssertionError.
1319 """
1320 try:
1321 assertion(*args, **kwargs)
1322 except AssertionError:
1323 raise KnownFailure(reason)
1324 else:
1325 self.fail('Unexpected success. Should have failed: %s' % reason)
1326
1327 def assertFileEqual(self, content, path):1286 def assertFileEqual(self, content, path):
1328 """Fail if path does not contain 'content'."""1287 """Fail if path does not contain 'content'."""
1329 self.failUnlessExists(path)1288 self.failUnlessExists(path)
@@ -1479,18 +1438,12 @@
14791438
1480 Close the file and delete it, unless setKeepLogfile was called.1439 Close the file and delete it, unless setKeepLogfile was called.
1481 """1440 """
1482 if self._log_file is None:1441 if bzrlib.trace._trace_file:
1483 return1442 # flush the log file, to get all content
1443 bzrlib.trace._trace_file.flush()
1484 bzrlib.trace.pop_log_file(self._log_memento)1444 bzrlib.trace.pop_log_file(self._log_memento)
1485 self._log_file.close()1445 # Cache the log result and delete the file on disk
1486 self._log_file = None1446 self._get_log(False)
1487 if not self._keep_log_file:
1488 os.remove(self._log_file_name)
1489 self._log_file_name = None
1490
1491 def setKeepLogfile(self):
1492 """Make the logfile not be deleted when _finishLogFile is called."""
1493 self._keep_log_file = True
14941447
1495 def thisFailsStrictLockCheck(self):1448 def thisFailsStrictLockCheck(self):
1496 """It is known that this test would fail with -Dstrict_locks.1449 """It is known that this test would fail with -Dstrict_locks.
@@ -1587,7 +1540,8 @@
1587 else:1540 else:
1588 addSkip(self, reason)1541 addSkip(self, reason)
15891542
1590 def _do_known_failure(self, result):1543 @staticmethod
1544 def _do_known_failure(self, result, e):
1591 err = sys.exc_info()1545 err = sys.exc_info()
1592 addExpectedFailure = getattr(result, 'addExpectedFailure', None)1546 addExpectedFailure = getattr(result, 'addExpectedFailure', None)
1593 if addExpectedFailure is not None:1547 if addExpectedFailure is not None:
@@ -1595,6 +1549,7 @@
1595 else:1549 else:
1596 result.addSuccess(self)1550 result.addSuccess(self)
15971551
1552 @staticmethod
1598 def _do_not_applicable(self, result, e):1553 def _do_not_applicable(self, result, e):
1599 if not e.args:1554 if not e.args:
1600 reason = 'No reason given'1555 reason = 'No reason given'
@@ -1606,120 +1561,15 @@
1606 else:1561 else:
1607 self._do_skip(result, reason)1562 self._do_skip(result, reason)
16081563
1609 def _do_unsupported_or_skip(self, result, reason):1564 @staticmethod
1565 def _do_unsupported_or_skip(self, result, e):
1566 reason = e.args[0]
1610 addNotSupported = getattr(result, 'addNotSupported', None)1567 addNotSupported = getattr(result, 'addNotSupported', None)
1611 if addNotSupported is not None:1568 if addNotSupported is not None:
1612 result.addNotSupported(self, reason)1569 result.addNotSupported(self, reason)
1613 else:1570 else:
1614 self._do_skip(result, reason)1571 self._do_skip(result, reason)
16151572
1616 def run(self, result=None):
1617 if result is None: result = self.defaultTestResult()
1618 result.startTest(self)
1619 try:
1620 self._run(result)
1621 return result
1622 finally:
1623 result.stopTest(self)
1624
1625 def _run(self, result):
1626 for feature in getattr(self, '_test_needs_features', []):
1627 if not feature.available():
1628 return self._do_unsupported_or_skip(result, feature)
1629 try:
1630 absent_attr = object()
1631 # Python 2.5
1632 method_name = getattr(self, '_testMethodName', absent_attr)
1633 if method_name is absent_attr:
1634 # Python 2.4
1635 method_name = getattr(self, '_TestCase__testMethodName')
1636 testMethod = getattr(self, method_name)
1637 try:
1638 try:
1639 self.setUp()
1640 if not self._bzr_test_setUp_run:
1641 self.fail(
1642 "test setUp did not invoke "
1643 "bzrlib.tests.TestCase's setUp")
1644 except KeyboardInterrupt:
1645 self._runCleanups()
1646 raise
1647 except KnownFailure:
1648 self._do_known_failure(result)
1649 self.tearDown()
1650 return
1651 except TestNotApplicable, e:
1652 self._do_not_applicable(result, e)
1653 self.tearDown()
1654 return
1655 except TestSkipped, e:
1656 self._do_skip(result, e.args[0])
1657 self.tearDown()
1658 return result
1659 except UnavailableFeature, e:
1660 self._do_unsupported_or_skip(result, e.args[0])
1661 self.tearDown()
1662 return
1663 except:
1664 result.addError(self, sys.exc_info())
1665 self._runCleanups()
1666 return result
1667
1668 ok = False
1669 try:
1670 testMethod()
1671 ok = True
1672 except KnownFailure:
1673 self._do_known_failure(result)
1674 except self.failureException:
1675 result.addFailure(self, sys.exc_info())
1676 except TestNotApplicable, e:
1677 self._do_not_applicable(result, e)
1678 except TestSkipped, e:
1679 if not e.args:
1680 reason = "No reason given."
1681 else:
1682 reason = e.args[0]
1683 self._do_skip(result, reason)
1684 except UnavailableFeature, e:
1685 self._do_unsupported_or_skip(result, e.args[0])
1686 except KeyboardInterrupt:
1687 self._runCleanups()
1688 raise
1689 except:
1690 result.addError(self, sys.exc_info())
1691
1692 try:
1693 self.tearDown()
1694 if not self._bzr_test_tearDown_run:
1695 self.fail(
1696 "test tearDown did not invoke "
1697 "bzrlib.tests.TestCase's tearDown")
1698 except KeyboardInterrupt:
1699 self._runCleanups()
1700 raise
1701 except:
1702 result.addError(self, sys.exc_info())
1703 self._runCleanups()
1704 ok = False
1705 if ok: result.addSuccess(self)
1706 return result
1707 except KeyboardInterrupt:
1708 self._runCleanups()
1709 raise
1710 finally:
1711 saved_attrs = {}
1712 for attr_name in self.attrs_to_keep:
1713 if attr_name in self.__dict__:
1714 saved_attrs[attr_name] = self.__dict__[attr_name]
1715 self.__dict__ = saved_attrs
1716
1717 def tearDown(self):
1718 self._runCleanups()
1719 self._log_contents = ''
1720 self._bzr_test_tearDown_run = True
1721 unittest.TestCase.tearDown(self)
1722
1723 def time(self, callable, *args, **kwargs):1573 def time(self, callable, *args, **kwargs):
1724 """Run callable and accrue the time it takes to the benchmark time.1574 """Run callable and accrue the time it takes to the benchmark time.
17251575
@@ -1728,6 +1578,8 @@
1728 self._benchcalls.1578 self._benchcalls.
1729 """1579 """
1730 if self._benchtime is None:1580 if self._benchtime is None:
1581 self.addDetail('benchtime', content.Content(content.ContentType(
1582 "text", "plain"), lambda:[str(self._benchtime)]))
1731 self._benchtime = 01583 self._benchtime = 0
1732 start = time.time()1584 start = time.time()
1733 try:1585 try:
@@ -1742,25 +1594,14 @@
1742 finally:1594 finally:
1743 self._benchtime += time.time() - start1595 self._benchtime += time.time() - start
17441596
1745 def _runCleanups(self):
1746 """Run registered cleanup functions.
1747
1748 This should only be called from TestCase.tearDown.
1749 """
1750 # TODO: Perhaps this should keep running cleanups even if
1751 # one of them fails?
1752
1753 # Actually pop the cleanups from the list so tearDown running
1754 # twice is safe (this happens for skipped tests).
1755 while self._cleanups:
1756 cleanup, args, kwargs = self._cleanups.pop()
1757 cleanup(*args, **kwargs)
1758
1759 def log(self, *args):1597 def log(self, *args):
1760 mutter(*args)1598 mutter(*args)
17611599
1762 def _get_log(self, keep_log_file=False):1600 def _get_log(self, keep_log_file=False):
1763 """Get the log from bzrlib.trace calls from this test.1601 """Internal helper to get the log from bzrlib.trace for this test.
1602
1603 Please use self.getDetails, or self.get_log to access this in test case
1604 code.
17641605
1765 :param keep_log_file: When True, if the log is still a file on disk1606 :param keep_log_file: When True, if the log is still a file on disk
1766 leave it as a file on disk. When False, if the log is still a file1607 leave it as a file on disk. When False, if the log is still a file
@@ -1768,21 +1609,33 @@
1768 self._log_contents.1609 self._log_contents.
1769 :return: A string containing the log.1610 :return: A string containing the log.
1770 """1611 """
1771 # flush the log file, to get all content1612 if self._log_contents is not None:
1613 try:
1614 self._log_contents.decode('utf8')
1615 except UnicodeDecodeError:
1616 unicodestr = self._log_contents.decode('utf8', 'replace')
1617 self._log_contents = unicodestr.encode('utf8')
1618 return self._log_contents
1772 import bzrlib.trace1619 import bzrlib.trace
1773 if bzrlib.trace._trace_file:1620 if bzrlib.trace._trace_file:
1621 # flush the log file, to get all content
1774 bzrlib.trace._trace_file.flush()1622 bzrlib.trace._trace_file.flush()
1775 if self._log_contents:
1776 # XXX: this can hardly contain the content flushed above --vila
1777 # 20080128
1778 return self._log_contents
1779 if self._log_file_name is not None:1623 if self._log_file_name is not None:
1780 logfile = open(self._log_file_name)1624 logfile = open(self._log_file_name)
1781 try:1625 try:
1782 log_contents = logfile.read()1626 log_contents = logfile.read()
1783 finally:1627 finally:
1784 logfile.close()1628 logfile.close()
1629 try:
1630 log_contents.decode('utf8')
1631 except UnicodeDecodeError:
1632 unicodestr = log_contents.decode('utf8', 'replace')
1633 log_contents = unicodestr.encode('utf8')
1785 if not keep_log_file:1634 if not keep_log_file:
1635 self._log_file.close()
1636 self._log_file = None
1637 # Permit multiple calls to get_log until we clean it up in
1638 # finishLogFile
1786 self._log_contents = log_contents1639 self._log_contents = log_contents
1787 try:1640 try:
1788 os.remove(self._log_file_name)1641 os.remove(self._log_file_name)
@@ -1792,9 +1645,17 @@
1792 ' %r\n' % self._log_file_name))1645 ' %r\n' % self._log_file_name))
1793 else:1646 else:
1794 raise1647 raise
1648 self._log_file_name = None
1795 return log_contents1649 return log_contents
1796 else:1650 else:
1797 return "DELETED log file to reduce memory footprint"1651 return "No log file content and no log file name."
1652
1653 def get_log(self):
1654 """Get a unicode string containing the log from bzrlib.trace.
1655
1656 Undecodable characters are replaced.
1657 """
1658 return u"".join(self.getDetails()['log'].iter_text())
17981659
1799 def requireFeature(self, feature):1660 def requireFeature(self, feature):
1800 """This test requires a specific feature is available.1661 """This test requires a specific feature is available.
@@ -3219,7 +3080,7 @@
3219 if self.randomised:3080 if self.randomised:
3220 return iter(self._tests)3081 return iter(self._tests)
3221 self.randomised = True3082 self.randomised = True
3222 self.stream.writeln("Randomizing test order using seed %s\n" %3083 self.stream.write("Randomizing test order using seed %s\n\n" %
3223 (self.actual_seed()))3084 (self.actual_seed()))
3224 # Initialise the random number generator.3085 # Initialise the random number generator.
3225 random.seed(self.actual_seed())3086 random.seed(self.actual_seed())
@@ -3310,7 +3171,7 @@
3310 sys.stdin.close()3171 sys.stdin.close()
3311 sys.stdin = None3172 sys.stdin = None
3312 stream = os.fdopen(c2pwrite, 'wb', 1)3173 stream = os.fdopen(c2pwrite, 'wb', 1)
3313 subunit_result = BzrAutoTimingTestResultDecorator(3174 subunit_result = AutoTimingTestResultDecorator(
3314 TestProtocolClient(stream))3175 TestProtocolClient(stream))
3315 process_suite.run(subunit_result)3176 process_suite.run(subunit_result)
3316 finally:3177 finally:
@@ -3408,56 +3269,10 @@
34083269
3409 def addFailure(self, test, err):3270 def addFailure(self, test, err):
3410 self.result.addFailure(test, err)3271 self.result.addFailure(test, err)
34113272ForwardingResult = testtools.ExtendedToOriginalDecorator
34123273
3413class BZRTransformingResult(ForwardingResult):3274
34143275from subunit.test_results import AutoTimingTestResultDecorator
3415 def addError(self, test, err):
3416 feature = self._error_looks_like('UnavailableFeature: ', err)
3417 if feature is not None:
3418 self.result.addNotSupported(test, feature)
3419 else:
3420 self.result.addError(test, err)
3421
3422 def addFailure(self, test, err):
3423 known = self._error_looks_like('KnownFailure: ', err)
3424 if known is not None:
3425 self.result.addExpectedFailure(test,
3426 [KnownFailure, KnownFailure(known), None])
3427 else:
3428 self.result.addFailure(test, err)
3429
3430 def _error_looks_like(self, prefix, err):
3431 """Deserialize exception and returns the stringify value."""
3432 import subunit
3433 value = None
3434 typ, exc, _ = err
3435 if isinstance(exc, subunit.RemoteException):
3436 # stringify the exception gives access to the remote traceback
3437 # We search the last line for 'prefix'
3438 lines = str(exc).split('\n')
3439 while lines and not lines[-1]:
3440 lines.pop(-1)
3441 if lines:
3442 if lines[-1].startswith(prefix):
3443 value = lines[-1][len(prefix):]
3444 return value
3445
3446
3447try:
3448 from subunit.test_results import AutoTimingTestResultDecorator
3449 # Expected failure should be seen as a success not a failure Once subunit
3450 # provide native support for that, BZRTransformingResult and this class
3451 # will become useless.
3452 class BzrAutoTimingTestResultDecorator(AutoTimingTestResultDecorator):
3453
3454 def addExpectedFailure(self, test, err):
3455 self._before_event()
3456 return self._call_maybe("addExpectedFailure", self._degrade_skip,
3457 test, err)
3458except ImportError:
3459 # Let's just define a no-op decorator
3460 BzrAutoTimingTestResultDecorator = lambda x:x
34613276
34623277
3463class ProfileResult(ForwardingResult):3278class ProfileResult(ForwardingResult):
@@ -4478,7 +4293,7 @@
4478 from subunit import TestProtocolClient4293 from subunit import TestProtocolClient
4479 class SubUnitBzrRunner(TextTestRunner):4294 class SubUnitBzrRunner(TextTestRunner):
4480 def run(self, test):4295 def run(self, test):
4481 result = BzrAutoTimingTestResultDecorator(4296 result = AutoTimingTestResultDecorator(
4482 TestProtocolClient(self.stream))4297 TestProtocolClient(self.stream))
4483 test.run(result)4298 test.run(result)
4484 return result4299 return result
44854300
=== modified file 'bzrlib/tests/blackbox/test_debug.py'
--- bzrlib/tests/blackbox/test_debug.py 2009-03-23 14:59:43 +0000
+++ bzrlib/tests/blackbox/test_debug.py 2009-12-17 09:43:14 +0000
@@ -38,5 +38,4 @@
38 def test_dash_dlock(self):38 def test_dash_dlock(self):
39 # With -Dlock, locking and unlocking is recorded into the log39 # With -Dlock, locking and unlocking is recorded into the log
40 self.run_bzr("-Dlock init foo")40 self.run_bzr("-Dlock init foo")
41 trace_messages = self._get_log(keep_log_file=True)41 self.assertContainsRe(self.get_log(), "lock_write")
42 self.assertContainsRe(trace_messages, "lock_write")
4342
=== modified file 'bzrlib/tests/blackbox/test_exceptions.py'
--- bzrlib/tests/blackbox/test_exceptions.py 2009-08-20 06:25:02 +0000
+++ bzrlib/tests/blackbox/test_exceptions.py 2009-12-17 09:43:14 +0000
@@ -55,8 +55,7 @@
55 os.mkdir('foo')55 os.mkdir('foo')
56 bzrdir.BzrDirFormat5().initialize('foo')56 bzrdir.BzrDirFormat5().initialize('foo')
57 out, err = self.run_bzr("status foo")57 out, err = self.run_bzr("status foo")
58 self.assertContainsRe(self._get_log(keep_log_file=True),58 self.assertContainsRe(self.get_log(), "bzr upgrade")
59 "bzr upgrade")
60 finally:59 finally:
61 repository._deprecation_warning_done = True60 repository._deprecation_warning_done = True
6261
6362
=== modified file 'bzrlib/tests/per_pack_repository.py'
--- bzrlib/tests/per_pack_repository.py 2009-09-07 03:35:06 +0000
+++ bzrlib/tests/per_pack_repository.py 2009-12-17 09:43:14 +0000
@@ -687,9 +687,9 @@
687 # abort_write_group will not raise an error687 # abort_write_group will not raise an error
688 self.assertEqual(None, repo.abort_write_group(suppress_errors=True))688 self.assertEqual(None, repo.abort_write_group(suppress_errors=True))
689 # But it does log an error689 # But it does log an error
690 log_file = self._get_log(keep_log_file=True)690 log = self.get_log()
691 self.assertContainsRe(log_file, 'abort_write_group failed')691 self.assertContainsRe(log, 'abort_write_group failed')
692 self.assertContainsRe(log_file, r'INFO bzr: ERROR \(ignored\):')692 self.assertContainsRe(log, r'INFO bzr: ERROR \(ignored\):')
693 if token is not None:693 if token is not None:
694 repo.leave_lock_in_place()694 repo.leave_lock_in_place()
695695
696696
=== modified file 'bzrlib/tests/per_repository/test_check.py'
--- bzrlib/tests/per_repository/test_check.py 2009-08-30 23:51:10 +0000
+++ bzrlib/tests/per_repository/test_check.py 2009-12-17 09:43:14 +0000
@@ -46,8 +46,7 @@
46 revid2 = tree.commit('2')46 revid2 = tree.commit('2')
47 check_object = tree.branch.repository.check([revid1, revid2])47 check_object = tree.branch.repository.check([revid1, revid2])
48 check_object.report_results(verbose=True)48 check_object.report_results(verbose=True)
49 log = self._get_log(keep_log_file=True)49 self.assertContainsRe(self.get_log(), "0 unreferenced text versions")
50 self.assertContainsRe(log, "0 unreferenced text versions")
5150
5251
53class TestFindInconsistentRevisionParents(TestCaseWithBrokenRevisionIndex):52class TestFindInconsistentRevisionParents(TestCaseWithBrokenRevisionIndex):
@@ -90,13 +89,11 @@
90 # contents of it!89 # contents of it!
91 check_object = repo.check(['ignored'])90 check_object = repo.check(['ignored'])
92 check_object.report_results(verbose=False)91 check_object.report_results(verbose=False)
93 log = self._get_log(keep_log_file=True)92 self.assertContainsRe(self.get_log(),
94 self.assertContainsRe(93 '1 revisions have incorrect parents in the revision index')
95 log, '1 revisions have incorrect parents in the revision index')
96 check_object.report_results(verbose=True)94 check_object.report_results(verbose=True)
97 log = self._get_log(keep_log_file=True)
98 self.assertContainsRe(95 self.assertContainsRe(
99 log,96 self.get_log(),
100 "revision-id has wrong parents in index: "97 "revision-id has wrong parents in index: "
101 r"\('incorrect-parent',\) should be \(\)")98 r"\('incorrect-parent',\) should be \(\)")
10299
@@ -147,5 +144,5 @@
147 rev_id = builder.commit('first post')144 rev_id = builder.commit('first post')
148 result = repo.check(None, check_repo=True)145 result = repo.check(None, check_repo=True)
149 result.report_results(True)146 result.report_results(True)
150 log = self._get_log(keep_log_file=True)147 log = self.get_log()
151 self.assertFalse('Missing' in log, "Something was missing in %r" % log)148 self.assertFalse('Missing' in log, "Something was missing in %r" % log)
152149
=== modified file 'bzrlib/tests/per_repository/test_check_reconcile.py'
--- bzrlib/tests/per_repository/test_check_reconcile.py 2009-03-23 14:59:43 +0000
+++ bzrlib/tests/per_repository/test_check_reconcile.py 2009-12-17 09:43:14 +0000
@@ -198,10 +198,9 @@
198 repo, scenario = self.prepare_test_repository()198 repo, scenario = self.prepare_test_repository()
199 check_result = repo.check()199 check_result = repo.check()
200 check_result.report_results(verbose=True)200 check_result.report_results(verbose=True)
201 log = self.get_log()
201 for pattern in scenario.check_regexes(repo):202 for pattern in scenario.check_regexes(repo):
202 self.assertContainsRe(203 self.assertContainsRe(log, pattern)
203 self._get_log(keep_log_file=True),
204 pattern)
205204
206 def test_find_text_key_references(self):205 def test_find_text_key_references(self):
207 """Test that find_text_key_references finds erroneous references."""206 """Test that find_text_key_references finds erroneous references."""
208207
=== modified file 'bzrlib/tests/per_repository_reference/test_check.py'
--- bzrlib/tests/per_repository_reference/test_check.py 2009-08-11 05:26:57 +0000
+++ bzrlib/tests/per_repository_reference/test_check.py 2009-12-17 09:43:14 +0000
@@ -39,5 +39,4 @@
39 check_result = referring.branch.repository.check(39 check_result = referring.branch.repository.check(
40 referring.branch.repository.all_revision_ids())40 referring.branch.repository.all_revision_ids())
41 check_result.report_results(verbose=False)41 check_result.report_results(verbose=False)
42 log = self._get_log(keep_log_file=True)42 self.assertFalse("inconsistent parents" in self.get_log())
43 self.assertFalse("inconsistent parents" in log)
4443
=== modified file 'bzrlib/tests/test_cleanup.py'
--- bzrlib/tests/test_cleanup.py 2009-10-26 06:23:14 +0000
+++ bzrlib/tests/test_cleanup.py 2009-12-17 09:43:14 +0000
@@ -39,8 +39,7 @@
39 self.call_log.append('no_op_cleanup')39 self.call_log.append('no_op_cleanup')
4040
41 def assertLogContains(self, regex):41 def assertLogContains(self, regex):
42 log = self._get_log(keep_log_file=True)42 self.assertContainsRe(self.get_log(), regex, re.DOTALL)
43 self.assertContainsRe(log, regex, re.DOTALL)
4443
45 def failing_cleanup(self):44 def failing_cleanup(self):
46 self.call_log.append('failing_cleanup')45 self.call_log.append('failing_cleanup')
@@ -184,8 +183,7 @@
184 self.assertRaises(ErrorA, _do_with_cleanups, cleanups,183 self.assertRaises(ErrorA, _do_with_cleanups, cleanups,
185 self.trivial_func)184 self.trivial_func)
186 self.assertLogContains('Cleanup failed:.*ErrorB')185 self.assertLogContains('Cleanup failed:.*ErrorB')
187 log = self._get_log(keep_log_file=True)186 self.assertFalse('ErrorA' in self.get_log())
188 self.assertFalse('ErrorA' in log)
189187
190 def make_two_failing_cleanup_funcs(self):188 def make_two_failing_cleanup_funcs(self):
191 def raise_a():189 def raise_a():
192190
=== modified file 'bzrlib/tests/test_config.py'
--- bzrlib/tests/test_config.py 2009-10-31 01:43:48 +0000
+++ bzrlib/tests/test_config.py 2009-12-17 09:43:14 +0000
@@ -1600,7 +1600,7 @@
1600 self.assertEquals(entered_password,1600 self.assertEquals(entered_password,
1601 conf.get_password('ssh', 'bar.org', user='jim'))1601 conf.get_password('ssh', 'bar.org', user='jim'))
1602 self.assertContainsRe(1602 self.assertContainsRe(
1603 self._get_log(keep_log_file=True),1603 self.get_log(),
1604 'password ignored in section \[ssh with password\]')1604 'password ignored in section \[ssh with password\]')
16051605
1606 def test_ssh_without_password_doesnt_emit_warning(self):1606 def test_ssh_without_password_doesnt_emit_warning(self):
@@ -1625,7 +1625,7 @@
1625 # No warning shoud be emitted since there is no password. We are only1625 # No warning shoud be emitted since there is no password. We are only
1626 # providing "user".1626 # providing "user".
1627 self.assertNotContainsRe(1627 self.assertNotContainsRe(
1628 self._get_log(keep_log_file=True),1628 self.get_log(),
1629 'password ignored in section \[ssh with password\]')1629 'password ignored in section \[ssh with password\]')
16301630
1631 def test_uses_fallback_stores(self):1631 def test_uses_fallback_stores(self):
16321632
=== modified file 'bzrlib/tests/test_http_response.py'
--- bzrlib/tests/test_http_response.py 2009-03-23 14:59:43 +0000
+++ bzrlib/tests/test_http_response.py 2009-12-17 09:43:14 +0000
@@ -96,8 +96,7 @@
96 # Override the thresold to force the warning emission96 # Override the thresold to force the warning emission
97 conn._range_warning_thresold = 6 # There are 7 bytes pending97 conn._range_warning_thresold = 6 # There are 7 bytes pending
98 conn.cleanup_pipe()98 conn.cleanup_pipe()
99 self.assertContainsRe(self._get_log(keep_log_file=True),99 self.assertContainsRe(self.get_log(), 'Got a 200 response when asking')
100 'Got a 200 response when asking')
101100
102101
103class TestRangeFileMixin(object):102class TestRangeFileMixin(object):
104103
=== modified file 'bzrlib/tests/test_merge.py'
--- bzrlib/tests/test_merge.py 2009-12-10 17:16:19 +0000
+++ bzrlib/tests/test_merge.py 2009-12-17 09:43:14 +0000
@@ -152,13 +152,12 @@
152 log = StringIO()152 log = StringIO()
153 merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),153 merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),
154 this_tree=tree_b, ignore_zero=True)154 this_tree=tree_b, ignore_zero=True)
155 log = self._get_log(keep_log_file=True)155 self.failUnless('All changes applied successfully.\n' not in
156 self.failUnless('All changes applied successfully.\n' not in log)156 self.get_log())
157 tree_b.revert()157 tree_b.revert()
158 merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),158 merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),
159 this_tree=tree_b, ignore_zero=False)159 this_tree=tree_b, ignore_zero=False)
160 log = self._get_log(keep_log_file=True)160 self.failUnless('All changes applied successfully.\n' in self.get_log())
161 self.failUnless('All changes applied successfully.\n' in log)
162161
163 def test_merge_inner_conflicts(self):162 def test_merge_inner_conflicts(self):
164 tree_a = self.make_branch_and_tree('a')163 tree_a = self.make_branch_and_tree('a')
165164
=== modified file 'bzrlib/tests/test_remote.py'
--- bzrlib/tests/test_remote.py 2009-12-03 20:34:03 +0000
+++ bzrlib/tests/test_remote.py 2009-12-17 09:43:14 +0000
@@ -2893,7 +2893,7 @@
2893 # In addition to re-raising ErrorFromSmartServer, some debug info has2893 # In addition to re-raising ErrorFromSmartServer, some debug info has
2894 # been muttered to the log file for developer to look at.2894 # been muttered to the log file for developer to look at.
2895 self.assertContainsRe(2895 self.assertContainsRe(
2896 self._get_log(keep_log_file=True),2896 self.get_log(),
2897 "Missing key 'branch' in context")2897 "Missing key 'branch' in context")
28982898
2899 def test_path_missing(self):2899 def test_path_missing(self):
@@ -2907,8 +2907,7 @@
2907 self.assertEqual(server_error, translated_error)2907 self.assertEqual(server_error, translated_error)
2908 # In addition to re-raising ErrorFromSmartServer, some debug info has2908 # In addition to re-raising ErrorFromSmartServer, some debug info has
2909 # been muttered to the log file for developer to look at.2909 # been muttered to the log file for developer to look at.
2910 self.assertContainsRe(2910 self.assertContainsRe(self.get_log(), "Missing key 'path' in context")
2911 self._get_log(keep_log_file=True), "Missing key 'path' in context")
29122911
29132912
2914class TestStacking(tests.TestCaseWithTransport):2913class TestStacking(tests.TestCaseWithTransport):
29152914
=== modified file 'bzrlib/tests/test_selftest.py'
--- bzrlib/tests/test_selftest.py 2009-12-08 21:46:07 +0000
+++ bzrlib/tests/test_selftest.py 2009-12-17 09:43:14 +0000
@@ -17,6 +17,7 @@
17"""Tests for the test framework."""17"""Tests for the test framework."""
1818
19from cStringIO import StringIO19from cStringIO import StringIO
20from doctest import ELLIPSIS
20import os21import os
21import signal22import signal
22import sys23import sys
@@ -24,6 +25,14 @@
24import unittest25import unittest
25import warnings26import warnings
2627
28from testtools import MultiTestResult
29from testtools.content_type import ContentType
30from testtools.matchers import (
31 DocTestMatches,
32 Equals,
33 )
34import testtools.tests.helpers
35
27import bzrlib36import bzrlib
28from bzrlib import (37from bzrlib import (
29 branchbuilder,38 branchbuilder,
@@ -78,14 +87,19 @@
78 TestUtil._load_module_by_name,87 TestUtil._load_module_by_name,
79 'bzrlib.no-name-yet')88 'bzrlib.no-name-yet')
8089
90
81class MetaTestLog(tests.TestCase):91class MetaTestLog(tests.TestCase):
8292
83 def test_logging(self):93 def test_logging(self):
84 """Test logs are captured when a test fails."""94 """Test logs are captured when a test fails."""
85 self.log('a test message')95 self.log('a test message')
86 self._log_file.flush()96 details = self.getDetails()
87 self.assertContainsRe(self._get_log(keep_log_file=True),97 log = details['log']
88 'a test message\n')98 self.assertThat(log.content_type, Equals(ContentType(
99 "text", "plain", {"charset": "utf8"})))
100 self.assertThat(u"".join(log.iter_text()), Equals(self.get_log()))
101 self.assertThat(self.get_log(),
102 DocTestMatches(u"...a test message\n", ELLIPSIS))
89103
90104
91class TestUnicodeFilename(tests.TestCase):105class TestUnicodeFilename(tests.TestCase):
@@ -687,7 +701,7 @@
687701
688 def test_profiles_tests(self):702 def test_profiles_tests(self):
689 self.requireFeature(test_lsprof.LSProfFeature)703 self.requireFeature(test_lsprof.LSProfFeature)
690 terminal = unittest.TestResult()704 terminal = testtools.tests.helpers.ExtendedTestResult()
691 result = tests.ProfileResult(terminal)705 result = tests.ProfileResult(terminal)
692 class Sample(tests.TestCase):706 class Sample(tests.TestCase):
693 def a(self):707 def a(self):
@@ -695,11 +709,11 @@
695 def sample_function(self):709 def sample_function(self):
696 pass710 pass
697 test = Sample("a")711 test = Sample("a")
698 test.attrs_to_keep = test.attrs_to_keep + ('_benchcalls',)
699 test.run(result)712 test.run(result)
700 self.assertLength(1, test._benchcalls)713 case = terminal._events[0][1]
714 self.assertLength(1, case._benchcalls)
701 # We must be able to unpack it as the test reporting code wants715 # We must be able to unpack it as the test reporting code wants
702 (_, _, _), stats = test._benchcalls[0]716 (_, _, _), stats = case._benchcalls[0]
703 self.assertTrue(callable(stats.pprint))717 self.assertTrue(callable(stats.pprint))
704718
705719
@@ -710,8 +724,10 @@
710 descriptions=0,724 descriptions=0,
711 verbosity=1,725 verbosity=1,
712 )726 )
713 test_case.run(result)727 capture = testtools.tests.helpers.ExtendedTestResult()
714 timed_string = result._testTimeString(test_case)728 test_case.run(MultiTestResult(result, capture))
729 run_case = capture._events[0][1]
730 timed_string = result._testTimeString(run_case)
715 self.assertContainsRe(timed_string, expected_re)731 self.assertContainsRe(timed_string, expected_re)
716732
717 def test_test_reporting(self):733 def test_test_reporting(self):
@@ -832,8 +848,8 @@
832 def stopTestRun(self): pass848 def stopTestRun(self): pass
833 def startTests(self): pass849 def startTests(self): pass
834 def report_test_start(self, test): pass850 def report_test_start(self, test): pass
835 def report_known_failure(self, test, err):851 def report_known_failure(self, test, err=None, details=None):
836 self._call = test, err852 self._call = test, 'known failure'
837 result = InstrumentedTestResult(None, None, None, None)853 result = InstrumentedTestResult(None, None, None, None)
838 class Test(tests.TestCase):854 class Test(tests.TestCase):
839 def test_function(self):855 def test_function(self):
@@ -842,9 +858,8 @@
842 test.run(result)858 test.run(result)
843 # it should invoke 'report_known_failure'.859 # it should invoke 'report_known_failure'.
844 self.assertEqual(2, len(result._call))860 self.assertEqual(2, len(result._call))
845 self.assertEqual(test, result._call[0])861 self.assertEqual(test.id(), result._call[0].id())
846 self.assertEqual(tests.KnownFailure, result._call[1][0])862 self.assertEqual('known failure', result._call[1])
847 self.assertIsInstance(result._call[1][1], tests.KnownFailure)
848 # we dont introspec the traceback, if the rest is ok, it would be863 # we dont introspec the traceback, if the rest is ok, it would be
849 # exceptional for it not to be.864 # exceptional for it not to be.
850 # it should update the known_failure_count on the object.865 # it should update the known_failure_count on the object.
@@ -944,7 +959,7 @@
944 test.run(result)959 test.run(result)
945 # it should invoke 'addNotSupported'.960 # it should invoke 'addNotSupported'.
946 self.assertEqual(2, len(result._call))961 self.assertEqual(2, len(result._call))
947 self.assertEqual(test, result._call[0])962 self.assertEqual(test.id(), result._call[0].id())
948 self.assertEqual(feature, result._call[1])963 self.assertEqual(feature, result._call[1])
949 # and not count as an error964 # and not count as an error
950 self.assertEqual(0, result.error_count)965 self.assertEqual(0, result.error_count)
@@ -1028,25 +1043,25 @@
1028 # the final output when real failures occur.1043 # the final output when real failures occur.
1029 class Test(tests.TestCase):1044 class Test(tests.TestCase):
1030 def known_failure_test(self):1045 def known_failure_test(self):
1031 raise tests.KnownFailure('failed')1046 self.expectFailure('failed', self.assertTrue, False)
1032 test = unittest.TestSuite()1047 test = unittest.TestSuite()
1033 test.addTest(Test("known_failure_test"))1048 test.addTest(Test("known_failure_test"))
1034 def failing_test():1049 def failing_test():
1035 raise AssertionError('foo')1050 self.fail('foo')
1036 test.addTest(unittest.FunctionTestCase(failing_test))1051 test.addTest(unittest.FunctionTestCase(failing_test))
1037 stream = StringIO()1052 stream = StringIO()
1038 runner = tests.TextTestRunner(stream=stream)1053 runner = tests.TextTestRunner(stream=stream)
1039 result = self.run_test_runner(runner, test)1054 result = self.run_test_runner(runner, test)
1040 lines = stream.getvalue().splitlines()1055 lines = stream.getvalue().splitlines()
1041 self.assertContainsRe(stream.getvalue(),1056 self.assertContainsRe(stream.getvalue(),
1042 '(?sm)^testing.*$'1057 '(?sm)^bzr selftest.*$'
1043 '.*'1058 '.*'
1044 '^======================================================================\n'1059 '^======================================================================\n'
1045 '^FAIL: unittest.FunctionTestCase \\(failing_test\\)\n'1060 '^FAIL: unittest.FunctionTestCase \\(failing_test\\)\n'
1046 '^----------------------------------------------------------------------\n'1061 '^----------------------------------------------------------------------\n'
1047 'Traceback \\(most recent call last\\):\n'1062 'Traceback \\(most recent call last\\):\n'
1048 ' .*' # File .*, line .*, in failing_test' - but maybe not from .pyc1063 ' .*' # File .*, line .*, in failing_test' - but maybe not from .pyc
1049 ' raise AssertionError\\(\'foo\'\\)\n'1064 ' self.fail\\(\'foo\'\\)\n'
1050 '.*'1065 '.*'
1051 '^----------------------------------------------------------------------\n'1066 '^----------------------------------------------------------------------\n'
1052 '.*'1067 '.*'
@@ -1058,7 +1073,7 @@
1058 # the final output.1073 # the final output.
1059 class Test(tests.TestCase):1074 class Test(tests.TestCase):
1060 def known_failure_test(self):1075 def known_failure_test(self):
1061 raise tests.KnownFailure('failed')1076 self.expectFailure('failed', self.assertTrue, False)
1062 test = Test("known_failure_test")1077 test = Test("known_failure_test")
1063 stream = StringIO()1078 stream = StringIO()
1064 runner = tests.TextTestRunner(stream=stream)1079 runner = tests.TextTestRunner(stream=stream)
@@ -1209,119 +1224,6 @@
1209 self.assertContainsRe(output_string, "--date [0-9.]+")1224 self.assertContainsRe(output_string, "--date [0-9.]+")
1210 self.assertLength(1, self._get_source_tree_calls)1225 self.assertLength(1, self._get_source_tree_calls)
12111226
1212 def assertLogDeleted(self, test):
1213 log = test._get_log()
1214 self.assertEqual("DELETED log file to reduce memory footprint", log)
1215 self.assertEqual('', test._log_contents)
1216 self.assertIs(None, test._log_file_name)
1217
1218 def test_success_log_deleted(self):
1219 """Successful tests have their log deleted"""
1220
1221 class LogTester(tests.TestCase):
1222
1223 def test_success(self):
1224 self.log('this will be removed\n')
1225
1226 sio = StringIO()
1227 runner = tests.TextTestRunner(stream=sio)
1228 test = LogTester('test_success')
1229 result = self.run_test_runner(runner, test)
1230
1231 self.assertLogDeleted(test)
1232
1233 def test_skipped_log_deleted(self):
1234 """Skipped tests have their log deleted"""
1235
1236 class LogTester(tests.TestCase):
1237
1238 def test_skipped(self):
1239 self.log('this will be removed\n')
1240 raise tests.TestSkipped()
1241
1242 sio = StringIO()
1243 runner = tests.TextTestRunner(stream=sio)
1244 test = LogTester('test_skipped')
1245 result = self.run_test_runner(runner, test)
1246
1247 self.assertLogDeleted(test)
1248
1249 def test_not_aplicable_log_deleted(self):
1250 """Not applicable tests have their log deleted"""
1251
1252 class LogTester(tests.TestCase):
1253
1254 def test_not_applicable(self):
1255 self.log('this will be removed\n')
1256 raise tests.TestNotApplicable()
1257
1258 sio = StringIO()
1259 runner = tests.TextTestRunner(stream=sio)
1260 test = LogTester('test_not_applicable')
1261 result = self.run_test_runner(runner, test)
1262
1263 self.assertLogDeleted(test)
1264
1265 def test_known_failure_log_deleted(self):
1266 """Know failure tests have their log deleted"""
1267
1268 class LogTester(tests.TestCase):
1269
1270 def test_known_failure(self):
1271 self.log('this will be removed\n')
1272 raise tests.KnownFailure()
1273
1274 sio = StringIO()
1275 runner = tests.TextTestRunner(stream=sio)
1276 test = LogTester('test_known_failure')
1277 result = self.run_test_runner(runner, test)
1278
1279 self.assertLogDeleted(test)
1280
1281 def test_fail_log_kept(self):
1282 """Failed tests have their log kept"""
1283
1284 class LogTester(tests.TestCase):
1285
1286 def test_fail(self):
1287 self.log('this will be kept\n')
1288 self.fail('this test fails')
1289
1290 sio = StringIO()
1291 runner = tests.TextTestRunner(stream=sio)
1292 test = LogTester('test_fail')
1293 result = self.run_test_runner(runner, test)
1294
1295 text = sio.getvalue()
1296 self.assertContainsRe(text, 'this will be kept')
1297 self.assertContainsRe(text, 'this test fails')
1298
1299 log = test._get_log()
1300 self.assertContainsRe(log, 'this will be kept')
1301 self.assertEqual(log, test._log_contents)
1302
1303 def test_error_log_kept(self):
1304 """Tests with errors have their log kept"""
1305
1306 class LogTester(tests.TestCase):
1307
1308 def test_error(self):
1309 self.log('this will be kept\n')
1310 raise ValueError('random exception raised')
1311
1312 sio = StringIO()
1313 runner = tests.TextTestRunner(stream=sio)
1314 test = LogTester('test_error')
1315 result = self.run_test_runner(runner, test)
1316
1317 text = sio.getvalue()
1318 self.assertContainsRe(text, 'this will be kept')
1319 self.assertContainsRe(text, 'random exception raised')
1320
1321 log = test._get_log()
1322 self.assertContainsRe(log, 'this will be kept')
1323 self.assertEqual(log, test._log_contents)
1324
1325 def test_startTestRun(self):1227 def test_startTestRun(self):
1326 """run should call result.startTestRun()"""1228 """run should call result.startTestRun()"""
1327 calls = []1229 calls = []
@@ -1491,6 +1393,7 @@
1491 self.assertEqual(set(['original-state']), bzrlib.debug.debug_flags)1393 self.assertEqual(set(['original-state']), bzrlib.debug.debug_flags)
14921394
1493 def make_test_result(self):1395 def make_test_result(self):
1396 """Get a test result that writes to the test log file."""
1494 return tests.TextTestResult(self._log_file, descriptions=0, verbosity=1)1397 return tests.TextTestResult(self._log_file, descriptions=0, verbosity=1)
14951398
1496 def inner_test(self):1399 def inner_test(self):
@@ -1504,6 +1407,7 @@
1504 result = self.make_test_result()1407 result = self.make_test_result()
1505 self.inner_test.run(result)1408 self.inner_test.run(result)
1506 note("outer finish")1409 note("outer finish")
1410 self.addCleanup(osutils.delete_any, self._log_file_name)
15071411
1508 def test_trace_nesting(self):1412 def test_trace_nesting(self):
1509 # this tests that each test case nests its trace facility correctly.1413 # this tests that each test case nests its trace facility correctly.
@@ -1521,7 +1425,6 @@
1521 outer_test = TestTestCase("outer_child")1425 outer_test = TestTestCase("outer_child")
1522 result = self.make_test_result()1426 result = self.make_test_result()
1523 outer_test.run(result)1427 outer_test.run(result)
1524 self.addCleanup(osutils.delete_any, outer_test._log_file_name)
1525 self.assertEqual(original_trace, bzrlib.trace._trace_file)1428 self.assertEqual(original_trace, bzrlib.trace._trace_file)
15261429
1527 def method_that_times_a_bit_twice(self):1430 def method_that_times_a_bit_twice(self):
@@ -1642,6 +1545,8 @@
1642 """Test disabled tests behaviour with support aware results."""1545 """Test disabled tests behaviour with support aware results."""
1643 test = SampleTestCase('_test_pass')1546 test = SampleTestCase('_test_pass')
1644 class DisabledFeature(object):1547 class DisabledFeature(object):
1548 def __eq__(self, other):
1549 return isinstance(other, DisabledFeature)
1645 def available(self):1550 def available(self):
1646 return False1551 return False
1647 the_feature = DisabledFeature()1552 the_feature = DisabledFeature()
@@ -1658,10 +1563,11 @@
1658 self.calls.append(('addNotSupported', test, feature))1563 self.calls.append(('addNotSupported', test, feature))
1659 result = InstrumentedTestResult()1564 result = InstrumentedTestResult()
1660 test.run(result)1565 test.run(result)
1566 case = result.calls[0][1]
1661 self.assertEqual([1567 self.assertEqual([
1662 ('startTest', test),1568 ('startTest', case),
1663 ('addNotSupported', test, the_feature),1569 ('addNotSupported', case, the_feature),
1664 ('stopTest', test),1570 ('stopTest', case),
1665 ],1571 ],
1666 result.calls)1572 result.calls)
16671573
@@ -2434,28 +2340,6 @@
2434 self.assertEqual('bzr: interrupted\n', result[1])2340 self.assertEqual('bzr: interrupted\n', result[1])
24352341
24362342
2437class TestKnownFailure(tests.TestCase):
2438
2439 def test_known_failure(self):
2440 """Check that KnownFailure is defined appropriately."""
2441 # a KnownFailure is an assertion error for compatability with unaware
2442 # runners.
2443 self.assertIsInstance(tests.KnownFailure(""), AssertionError)
2444
2445 def test_expect_failure(self):
2446 try:
2447 self.expectFailure("Doomed to failure", self.assertTrue, False)
2448 except tests.KnownFailure, e:
2449 self.assertEqual('Doomed to failure', e.args[0])
2450 try:
2451 self.expectFailure("Doomed to failure", self.assertTrue, True)
2452 except AssertionError, e:
2453 self.assertEqual('Unexpected success. Should have failed:'
2454 ' Doomed to failure', e.args[0])
2455 else:
2456 self.fail('Assertion not raised')
2457
2458
2459class TestFeature(tests.TestCase):2343class TestFeature(tests.TestCase):
24602344
2461 def test_caching(self):2345 def test_caching(self):
@@ -2697,8 +2581,7 @@
2697 # the test framework2581 # the test framework
2698 self.assertEquals('always fails', str(e))2582 self.assertEquals('always fails', str(e))
2699 # check that there's no traceback in the test log2583 # check that there's no traceback in the test log
2700 self.assertNotContainsRe(self._get_log(keep_log_file=True),2584 self.assertNotContainsRe(self.get_log(), r'Traceback')
2701 r'Traceback')
27022585
2703 def test_run_bzr_user_error_caught(self):2586 def test_run_bzr_user_error_caught(self):
2704 # Running bzr in blackbox mode, normal/expected/user errors should be2587 # Running bzr in blackbox mode, normal/expected/user errors should be
@@ -2957,14 +2840,12 @@
2957 def test_load_tests(self):2840 def test_load_tests(self):
2958 test_list = ['bzrlib.tests.test_sampler.DemoTest.test_nothing']2841 test_list = ['bzrlib.tests.test_sampler.DemoTest.test_nothing']
2959 loader = self._create_loader(test_list)2842 loader = self._create_loader(test_list)
2960
2961 suite = loader.loadTestsFromModuleName('bzrlib.tests.test_sampler')2843 suite = loader.loadTestsFromModuleName('bzrlib.tests.test_sampler')
2962 self.assertEquals(test_list, _test_ids(suite))2844 self.assertEquals(test_list, _test_ids(suite))
29632845
2964 def test_exclude_tests(self):2846 def test_exclude_tests(self):
2965 test_list = ['bogus']2847 test_list = ['bogus']
2966 loader = self._create_loader(test_list)2848 loader = self._create_loader(test_list)
2967
2968 suite = loader.loadTestsFromModuleName('bzrlib.tests.test_sampler')2849 suite = loader.loadTestsFromModuleName('bzrlib.tests.test_sampler')
2969 self.assertEquals([], _test_ids(suite))2850 self.assertEquals([], _test_ids(suite))
29702851
@@ -3015,8 +2896,8 @@
3015 tpr.register('bar', 'bbb.aaa.rrr')2896 tpr.register('bar', 'bbb.aaa.rrr')
3016 tpr.register('bar', 'bBB.aAA.rRR')2897 tpr.register('bar', 'bBB.aAA.rRR')
3017 self.assertEquals('bbb.aaa.rrr', tpr.get('bar'))2898 self.assertEquals('bbb.aaa.rrr', tpr.get('bar'))
3018 self.assertContainsRe(self._get_log(keep_log_file=True),2899 self.assertThat(self.get_log(),
3019 r'.*bar.*bbb.aaa.rrr.*bBB.aAA.rRR')2900 DocTestMatches("...bar...bbb.aaa.rrr...BB.aAA.rRR", ELLIPSIS))
30202901
3021 def test_get_unknown_prefix(self):2902 def test_get_unknown_prefix(self):
3022 tpr = self._get_registry()2903 tpr = self._get_registry()
30232904
=== modified file 'bzrlib/tests/test_trace.py'
--- bzrlib/tests/test_trace.py 2009-11-08 19:07:00 +0000
+++ bzrlib/tests/test_trace.py 2009-12-17 09:43:14 +0000
@@ -144,21 +144,21 @@
144 def test_trace_unicode(self):144 def test_trace_unicode(self):
145 """Write Unicode to trace log"""145 """Write Unicode to trace log"""
146 self.log(u'the unicode character for benzene is \N{BENZENE RING}')146 self.log(u'the unicode character for benzene is \N{BENZENE RING}')
147 self.assertContainsRe(self._get_log(keep_log_file=True),147 log = self.get_log()
148 "the unicode character for benzene is")148 self.assertContainsRe(log, "the unicode character for benzene is")
149149
150 def test_trace_argument_unicode(self):150 def test_trace_argument_unicode(self):
151 """Write a Unicode argument to the trace log"""151 """Write a Unicode argument to the trace log"""
152 mutter(u'the unicode character for benzene is %s', u'\N{BENZENE RING}')152 mutter(u'the unicode character for benzene is %s', u'\N{BENZENE RING}')
153 self.assertContainsRe(self._get_log(keep_log_file=True),153 log = self.get_log()
154 'the unicode character')154 self.assertContainsRe(log, 'the unicode character')
155155
156 def test_trace_argument_utf8(self):156 def test_trace_argument_utf8(self):
157 """Write a Unicode argument to the trace log"""157 """Write a Unicode argument to the trace log"""
158 mutter(u'the unicode character for benzene is %s',158 mutter(u'the unicode character for benzene is %s',
159 u'\N{BENZENE RING}'.encode('utf-8'))159 u'\N{BENZENE RING}'.encode('utf-8'))
160 self.assertContainsRe(self._get_log(keep_log_file=True),160 log = self.get_log()
161 'the unicode character')161 self.assertContainsRe(log, 'the unicode character')
162162
163 def test_report_broken_pipe(self):163 def test_report_broken_pipe(self):
164 try:164 try:
@@ -177,7 +177,7 @@
177 def test_mutter_callsite_1(self):177 def test_mutter_callsite_1(self):
178 """mutter_callsite can capture 1 level of stack frame."""178 """mutter_callsite can capture 1 level of stack frame."""
179 mutter_callsite(1, "foo %s", "a string")179 mutter_callsite(1, "foo %s", "a string")
180 log = self._get_log(keep_log_file=True)180 log = self.get_log()
181 # begin with the message181 # begin with the message
182 self.assertLogStartsWith(log, 'foo a string\nCalled from:\n')182 self.assertLogStartsWith(log, 'foo a string\nCalled from:\n')
183 # should show two frame: this frame and the one above183 # should show two frame: this frame and the one above
@@ -189,7 +189,7 @@
189 def test_mutter_callsite_2(self):189 def test_mutter_callsite_2(self):
190 """mutter_callsite can capture 2 levels of stack frame."""190 """mutter_callsite can capture 2 levels of stack frame."""
191 mutter_callsite(2, "foo %s", "a string")191 mutter_callsite(2, "foo %s", "a string")
192 log = self._get_log(keep_log_file=True)192 log = self.get_log()
193 # begin with the message193 # begin with the message
194 self.assertLogStartsWith(log, 'foo a string\nCalled from:\n')194 self.assertLogStartsWith(log, 'foo a string\nCalled from:\n')
195 # should show two frame: this frame and the one above195 # should show two frame: this frame and the one above
@@ -201,13 +201,19 @@
201 def test_mutter_never_fails(self):201 def test_mutter_never_fails(self):
202 # Even if the decode/encode stage fails, mutter should not202 # Even if the decode/encode stage fails, mutter should not
203 # raise an exception203 # raise an exception
204 # This test checks that mutter doesn't fail; the current behaviour
205 # is that it doesn't fail *and writes non-utf8*.
204 mutter(u'Writing a greek mu (\xb5) works in a unicode string')206 mutter(u'Writing a greek mu (\xb5) works in a unicode string')
205 mutter('But fails in an ascii string \xb5')207 mutter('But fails in an ascii string \xb5')
206 mutter('and in an ascii argument: %s', '\xb5')208 mutter('and in an ascii argument: %s', '\xb5')
207 log = self._get_log(keep_log_file=True)209 log = self.get_log()
208 self.assertContainsRe(log, 'Writing a greek mu')210 self.assertContainsRe(log, 'Writing a greek mu')
209 self.assertContainsRe(log, "But fails in an ascii string")211 self.assertContainsRe(log, "But fails in an ascii string")
210 self.assertContainsRe(log, u"ascii argument: \xb5")212 # However, the log content object does unicode replacement on reading
213 # to let it get unicode back where good data has been written. So we
214 # have to do a replaceent here as well.
215 self.assertContainsRe(log, "ascii argument: \xb5".decode('utf8',
216 'replace'))
211217
212 def test_push_log_file(self):218 def test_push_log_file(self):
213 """Can push and pop log file, and this catches mutter messages.219 """Can push and pop log file, and this catches mutter messages.
214220
=== modified file 'bzrlib/tests/test_transport_log.py'
--- bzrlib/tests/test_transport_log.py 2009-07-01 07:04:47 +0000
+++ bzrlib/tests/test_transport_log.py 2009-12-17 09:43:14 +0000
@@ -36,10 +36,9 @@
36 # operations such as mkdir are logged36 # operations such as mkdir are logged
37 mutter('where are you?')37 mutter('where are you?')
38 logging_transport.mkdir('subdir')38 logging_transport.mkdir('subdir')
39 self.assertContainsRe(self._get_log(True),39 log = self.get_log()
40 r'mkdir memory\+\d+://.*subdir')40 self.assertContainsRe(log, r'mkdir memory\+\d+://.*subdir')
41 self.assertContainsRe(self._get_log(True),41 self.assertContainsRe(log, ' --> None')
42 ' --> None')
43 # they have the expected effect42 # they have the expected effect
44 self.assertTrue(logging_transport.has('subdir'))43 self.assertTrue(logging_transport.has('subdir'))
45 # and they operate on the underlying transport44 # and they operate on the underlying transport