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 |
Related bugs: |
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 |
Commit message
Description of the change
Robert Collins (lifeless) wrote : | # |
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.
> + This permits features in testtools such as getUniqueInteger and
> + getUniqueString to be used. (Robert Collins)
> +
> * -Dhpssvfs will now trigger on ``RemoteBzrDir.
> 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://
Robert Collins (lifeless) wrote : | # |
I've added some polish and improved the news entry as per Martin's request.
Martin Packman (gz) wrote : | # |
Doesn't seem as big a code complexity win as I'd hope for adding a whole new dependency.
+traceback.
Not sure monkeypatching a builtin module in the middle of a several thousand line script is a good idea.
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.
>
> 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
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.
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://
iEYEARECAAYFAks
GiwAn3Jc9fkaAe2
=tUCZ
-----END PGP SIGNATURE-----
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.
Gordon Tyler (doxxx) wrote : | # |
Perhaps I should mention that I'm running this on Vista64 using a 32-bit version of ActivePython 2.6.
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/
> 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
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://
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
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/
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_
199 + try:
200 + return str(value)
201 + except:
Blanket except? That's scary.
290 + @staticmethod
291 + def _do_known_
299 + @staticmethod
300 def _do_not_
308 + @staticmethod
309 + def _do_unsupported
I should probably read the testtools docs or something, but... Static methods that take "self"?
597 === modified file 'bzrlib/
606 + log = u"".join(
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...
Martin Pool (mbp) wrote : | # |
8 +* New helper osutils.
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 UnicodeOrBytesT
+ log = u"".join(
Agree with Matt, this is ugly to repeat. Couldn't we have a convenience function for it?
Robert Collins (lifeless) wrote : | # |
On Wed, 2009-12-16 at 06:05 +0000, Matt Nordhoff wrote:
>
> 198 +def _clever_
> 199 + try:
> 200 + return str(value)
> 201 + except:
>
> Blanket except? That's scary.
Look at traceback._some_str :P.
-Rob
Robert Collins (lifeless) wrote : | # |
On Wed, 2009-12-16 at 06:09 +0000, Martin Pool wrote:
> Review: Needs Fixing
> 8 +* New helper osutils.
> 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 UnicodeOrBytesT
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(
>
> 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
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 BZRTransforming
back to addres the above :0) , thanks for that.
I'll be running this branch on babune and comment later about the results.
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.
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...
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
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.
--parallel=fork
bzr
selftest: /home/robertc/
/home/
bzr-2.1.0dev5 python-2.6.4
Linux-2.
-------
Ran 8 tests in 0.630s
OK
8 tests skipped
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
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://
iEYEARECAAYFAks
27QAoKW9xy6Mt+
=ycMf
-----END PGP SIGNATURE-----
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://
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
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.
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
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.
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:/
<https:/
--
Matt Nordhoff
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.
them.
-Rob
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:/
--
Martin <http://
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://
iEYEARECAAYFAks
RmgAoNG44xgHx+
=4Kc+
-----END PGP SIGNATURE-----
Preview Diff
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 |
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