On Nov 1, 2011, at 10:38 AM, exar...@twistedmatrix.com wrote:

> Hello,
> 
> I'd like for us to decide that we will introduce no new unit tests into 
> Twisted's test suite which use the global reactor.

+1.

Let's add this to the coding standard.  It doesn't look to me like anyone's 
objecting.

I think it might be worth considering a grandfather clause.  If we have an 
outstanding patch or branch which is otherwise close to being merged, whose 
only problem is real-reactor tests, I think it might not be worth it to block 
its landing at this point.  Instead, we could file a new ticket to fix its 
tests separately.

I don't know of any such outstanding tickets though, so hopefully this is a 
baseless concern.  And I'm happy to be overruled on this point, as well, if you 
feel strongly about it :).

> These tests require extra complexity in trial - to support the Deferred, 
> to check for left over event sources (incompletely implemented), and to 
> clean up left over event sources (incompletely implemented).

It might be useful to have a switch in trial that reports tests which return a 
Deferred, to make it easy to spot violators of this policy within Twisted.

I hope you're not suggesting getting rid of the feature _entirely_ from trial, 
though.  The first step to doing so would be to provide a totally 
comprehensive, complete, documented, maintained test fake for every interface 
supplied by every reactor... and then some.  I know that there are hundreds of 
tests for applications which _use_ Twisted that I've written which would be 
mind-bendingly difficult to rewrite using even a fake reactor.

For one simple example, on more than one occasion I've had a high-level 
abstraction wrapping a proprietary C program with spawnProcess and I wanted to 
test high-level logic that dealt with its results in specific cases; I could of 
course try to test everything using fakes, but it was actually harder to write 
the fakes than to set up the state for the real system, and the tests were 
significantly higher-value as regression tests with the real components 
integrated.

(In some cases it's a proprietary C extension module which needs to be run via 
deferToThread.)

That said, there are some caveats: none of these considerations should ever 
apply to Twisted itself; if they do seem to apply, then it's probably time to 
split that code out into a separate project.

Conversely, third-party projects should be able to implicitly rely on the 
reactor working as expected, and therefore don't have any concerns about 
testing the reactor itself.

If we could publicly expose the ReactorBuilder testing facility, and make it 
build only a single reactor by default, then it might be possible to test code 
like I described by spinning up a new reactor for each thing... but in some 
cases, there's an expensive subprocess that I really want multiple tests to 
share.  Would it be possible to move the associated file descriptors between 
reactors?  (On a related note, I wonder, how am I ever going to get those tests 
to work in disttrial...)

To summarize my own position about Trial, Deferreds, and real-reactor testing 
in general:

Whenever it's a reasonable amount of effort, tests should always be written 
against in-memory data structures and avoid doing real I/O, including any real 
asynchrony, since tests like that are easier to maintain and debug.
Within Twisted itself, it should always be a reasonable amount of effort to 
write tests the "right" way.  Any code which seems to require a 
real-asynchronous testing approach is either out of scope for Twisted or has 
other architectural problems which need to be addressed before landing it.
Some applications which use Twisted will need to interact with other systems as 
part of their unit tests, and they should be able to use the reactor to do 
that.  We should provide as many facilities to help people avoid writing tests 
like this as possible, but there are circumstances where we couldn't possibly 
provide enough, so we should continue to support trial's use as an integration 
testing tool.
We don't currently provide enough facilities to make it easy to avoid the 
reactor in all the cases where you should avoid it, and we should really 
improve proto_helpers et. al. so that it's easier to find in-memory 
implementations of stuff.

> The tests themselves are also complicated by the need to clean up those 
> event sources.

Amen.  Even today, the only document I'm aware of which actually covers this in 
its entirety is <http://blackjml.livejournal.com/23029.html>.  I think _maybe_ 
the trial tutorial actually covers all the steps but I'll need to read it very 
carefully to see if I can spot anything that it doesn't address :).

> The tests only exercise functionality against one reactor at a time 
> which leads to additional challenges for buildbot.

AMEN.  If we made all tests ReactorBuilder tests, would we be able to kill all 
the alternate-reactor (i.e. -r whatever) buildbots?  It seems like that would 
speed up build results quite a bit.

> If a test is for reactor functionality with multiple implementations, 
> the ReactorBuilder-style tests are better.  If a test is for 
> implementation details of a particular reactor, I think the necessary 
> parts of that reactor should just be invoked directly - on a new 
> instance.  For anything that's not a test for reactor functionality, no 
> reactor should be involved at all (protocol implementations, etc).

As I said above, we have a few gaps in this area which we need to work on 
filling.  For example, I recently wrote some tests where I wanted an 
IReactorCore provider but was dismayed to discover that Twisted doesn't provide 
an in-memory implementor of that interface anywhere, despite the fact that all 
I needed to call was addSystemEventTrigger/fireSystemEventTrigger, two APIs 
which just manipulate lists in memory even in their "real" implementations.  I 
was bad open-source citizen and did not immediately file a bug!  However, 
writing this email made me do so <http://twistedmatrix.com/trac/ticket/5353>.

While the code that I was writing wasn't for Twisted itself, I do feel like 
this may be an area where the prevalence of bad examples comes from the fact 
that we don't give our users a lot of options for nice, isolated testing.  It's 
definitely easier to just use the real reactor, despite its myriad issues, than 
to spend a week implementing comprehensive, testable versions of the ~40-odd 
interfaces in twisted.internet.interfaces.  But that means that all tx* 
projects tend to test things in the worse style, which means that most new 
patch contributors are likely to be cribbing from bad examples when writing a 
patch for Twisted.

So I filed another ticket for another frequent sticking point for me: 
<http://twistedmatrix.com/trac/ticket/5354>.  Hopefully if we can make more 
such things popular it will more widely disseminate the skills necessary to 
write tests in a more self-contained style.

> I've mentioned these ideas to various people at various times, but they 
> don't seem to be catching on, so I'd like to hear why and come to some 
> conclusion about how we're going to write tests in the future.

The new Trial tutorial provides a great resource that we were lacking for a 
long time, so we should make reference to its section on using StringTransport 
and Clock frequently and with enthusiasm.  

We a similar document for ReactorBuilder though.

I think we may want to also write up a companion wiki page that is more the 
rhetorical rather than pedagogic side of this problem, collecting our 
explanations of why you really want to use in-memory stuff instead of "real" 
reactors.  New contributors sometimes say that they don't feel like the code is 
"really" being tested unless they're testing against the real implementation of 
something, and it may not be immediately obvious that testing against a 
not-real implementation is desirable in many cases.  Much of it can just be 
copy/pasted from this thread :).
_______________________________________________
Twisted-Python mailing list
Twisted-Python@twistedmatrix.com
http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python

Reply via email to