Merge lp:~mandel/ubuntuone-dev-tools/domain-sockets into lp:ubuntuone-dev-tools

Proposed by Manuel de la Peña on 2012-04-26
Status: Merged
Merged at revision: 68
Proposed branch: lp:~mandel/ubuntuone-dev-tools/domain-sockets
Merge into: lp:ubuntuone-dev-tools
Diff against target: 542 lines (+215/-72)
3 files modified
ubuntuone/devtools/testcases/tests/test_txsocketserver.py (+81/-39)
ubuntuone/devtools/testcases/txsocketserver.py (+133/-32)
ubuntuone/devtools/testing/txwebserver.py (+1/-1)
To merge this branch: bzr merge lp:~mandel/ubuntuone-dev-tools/domain-sockets
Reviewer Review Type Date Requested Status
Brian Curtin (community) Approve on 2012-04-26
Alejandro J. Cura (community) 2012-04-26 Approve on 2012-04-26
Review via email: mp+103660@code.launchpad.net

Commit Message

- Added support for servers that use domain sockets as their transport (LP: #988257).

Description of the Change

- Added support for servers that use domain sockets as their transport (LP: #988257).

You will see that the tests are ran twice, once using tcp sockets and the second one using unix domain sockets. The use of endpoints has forced me to change the API because endpoints already returns the on connection made deferred. I'll be working on updating all the projects to ensure that this change is taken into account.

To post a comment you must log in.
Alejandro J. Cura (alecu) wrote :

Nice branch; code looks mostly fine, but here are some fixes I'm requesting:

---

I don't understand this comment:
        # do not point to a dir because the path will be too long

---

A few typos:
TCPNoConnetionTrackingTestCase -> TCPNoConnectionTrackingTestCase
UnixNoConnetionTrackingTestCase -> UnixNoConnectionTrackingTestCase
"Unix domian sockets" -> "Unix domain sockets"
"perse" -> "per se"
"client_endpoint_patter" -> "client_endpoint_pattern"

---

We don't need this docstring copied everywhere; having it only on the parent class is enough:
""" This test class is not testing the server and client perse but testing
    that we can use a PbServerFactory and PbClientFactory in a way that the
    connection will be closed correctly.
"""

---

Why are the UNIX client and server endpoint strings different?

    client_endpoint_patter = 'unix:path=%s'
    server_endpoint_pattern = 'unix:%s'

---

review: Needs Fixing
Alejandro J. Cura (alecu) wrote :

Looks good

review: Approve
Brian Curtin (brian.curtin) wrote :

Fine on Windows. Approved.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== renamed file 'ubuntuone/devtools/testcases/tests/test_txtcpserver.py' => 'ubuntuone/devtools/testcases/tests/test_txsocketserver.py'
2--- ubuntuone/devtools/testcases/tests/test_txtcpserver.py 2012-04-13 16:43:08 +0000
3+++ ubuntuone/devtools/testcases/tests/test_txsocketserver.py 2012-04-26 14:30:44 +0000
4@@ -32,11 +32,14 @@
5 from twisted.spread import pb
6 from twisted.trial.unittest import TestCase
7
8-from ubuntuone.devtools.testcases.txtcpserver import (
9+from ubuntuone.devtools.testcases import skipIfOS
10+from ubuntuone.devtools.testcases.txsocketserver import (
11 client_protocol_factory,
12 server_protocol_factory,
13- PbServerTestCase,
14+ ServerTestCase,
15+ TCPPbServerTestCase,
16 TidyTCPServer,
17+ TidyUnixServer,
18 TCPServerTestCase,
19 )
20
21@@ -185,8 +188,6 @@
22 protocol_instance.connectionMade()
23 self.assertIn('connectionMade', self.called,
24 'Super connectionMade must be called')
25- self.assertTrue(
26- protocol_instance.factory.testserver_on_connection_made.called)
27
28 # factory outside init
29 def test_connection_made_called(self):
30@@ -206,10 +207,10 @@
31 # pylint: enable=E1101
32
33
34-class TestPlainTwistedTestCase(TCPServerTestCase):
35+class TCPPlainTwistedTestCase(ServerTestCase):
36 """Test using a server.
37
38- This test class is not testing the server and client perse but testing
39+ This test class is not testing the server and client per se but testing
40 that we can use a PbServerFactory and PbClientFactory in a way that the
41 connection will be closed correctly.
42 """
43@@ -217,12 +218,15 @@
44 @defer.inlineCallbacks
45 def setUp(self):
46 """Set the diff tests."""
47- yield super(TestPlainTwistedTestCase, self).setUp()
48+ yield super(TCPPlainTwistedTestCase, self).setUp()
49 self.adder = Adder()
50 self.calculator = Calculator(self.adder)
51- self.listen_server(pb.PBServerFactory, self.calculator)
52- self.connect_client(pb.PBClientFactory)
53- yield self.client_connected
54+ yield self.listen_server(pb.PBServerFactory, self.calculator)
55+ yield self.connect_client(pb.PBClientFactory)
56+
57+ def get_server(self):
58+ """Return the server to be used to run the tests."""
59+ return TidyTCPServer()
60
61 @defer.inlineCallbacks
62 def test_addition(self):
63@@ -243,7 +247,16 @@
64 self.assertTrue(check)
65
66
67-class TestNoConnetionTrackingTestCase(TCPServerTestCase):
68+@skipIfOS('win32', 'Unix domain sockets not supported on windows.')
69+class UnixPlainTwistedTestCase(TCPPlainTwistedTestCase):
70+ """Test using a server over domain sockets."""
71+
72+ def get_server(self):
73+ """Return the server to be used to run the tests."""
74+ return TidyUnixServer()
75+
76+
77+class TCPNoConnectionTrackingTestCase(TCPServerTestCase):
78 """Test using a server.
79
80 This test class is not testing the server and the client perse but testing
81@@ -255,15 +268,12 @@
82 @defer.inlineCallbacks
83 def setUp(self):
84 """Set the diff tests."""
85- yield super(TestNoConnetionTrackingTestCase, self).setUp()
86+ yield super(TCPNoConnectionTrackingTestCase, self).setUp()
87 self.adder = Adder()
88 self.calculator = Calculator(self.adder)
89 # connect client and server
90- self.listen_server(pb.PBServerFactory, self.calculator)
91- self.connect_client(pb.PBClientFactory)
92-
93- # ensure we are connected
94- yield self.client_connected
95+ yield self.listen_server(pb.PBServerFactory, self.calculator)
96+ yield self.connect_client(pb.PBClientFactory)
97
98 self.first_number = 1
99 self.second_number = 2
100@@ -301,7 +311,17 @@
101 self.assertTrue(check)
102
103
104-class TestPlainPbTestCase(PbServerTestCase):
105+@skipIfOS('win32', 'Unix domain sockets not supported on windows.')
106+class UnixNoConnectionTrackingTestCase(TCPNoConnectionTrackingTestCase):
107+ """Test using a server over domain sockets."""
108+
109+ def get_server(self):
110+ """Return the server to be used to run the tests."""
111+ # do not point to a dir because the path will be too long
112+ return TidyUnixServer()
113+
114+
115+class TCPPlainPbTestCase(TCPPbServerTestCase):
116 """Test using a server.
117
118 This test class is not testing the server and client perse but testing
119@@ -312,12 +332,11 @@
120 @defer.inlineCallbacks
121 def setUp(self):
122 """Set the diff tests."""
123- yield super(TestPlainPbTestCase, self).setUp()
124+ yield super(TCPPlainPbTestCase, self).setUp()
125 self.adder = Adder()
126 self.calculator = Calculator(self.adder)
127- self.listen_server(self.calculator)
128- self.connect_client()
129- yield self.client_connected
130+ yield self.listen_server(self.calculator)
131+ yield self.connect_client()
132
133 @defer.inlineCallbacks
134 def test_addition(self):
135@@ -338,7 +357,16 @@
136 self.assertTrue(check)
137
138
139-class MultipleServersTestCase(TestCase):
140+@skipIfOS('win32', 'Unix domain sockets not supported on windows.')
141+class UnixPlainPbTestCase(TCPPlainPbTestCase):
142+ """Test using a server over domain sockets."""
143+
144+ def get_server(self):
145+ """Return the server to be used to run the tests."""
146+ return TidyUnixServer()
147+
148+
149+class TCPMultipleServersTestCase(TestCase):
150 """Ensure that several servers can be ran."""
151
152 timeout = 2
153@@ -346,24 +374,27 @@
154 @defer.inlineCallbacks
155 def setUp(self):
156 """Set the diff tests."""
157- yield super(MultipleServersTestCase, self).setUp()
158- self.first_tcp_server = TidyTCPServer()
159- self.second_tcp_server = TidyTCPServer()
160+ yield super(TCPMultipleServersTestCase, self).setUp()
161+ self.first_tcp_server = self.get_server()
162+ self.second_tcp_server = self.get_server()
163 self.adder = Adder()
164 self.calculator = Calculator(self.adder)
165 self.echoer = Echoer()
166
167+ def get_server(self):
168+ """Return the server to be used to run the tests."""
169+ return TidyTCPServer()
170+
171 @defer.inlineCallbacks
172 def test_single_server(self):
173 """Test setting a single server."""
174 first_number = 1
175 second_number = 2
176- self.first_tcp_server.listen_server(pb.PBServerFactory,
177+ yield self.first_tcp_server.listen_server(pb.PBServerFactory,
178 self.calculator)
179 self.addCleanup(self.first_tcp_server.clean_up)
180- calculator_c = self.first_tcp_server.connect_client(pb.PBClientFactory)
181- # ensure we do have connected
182- yield calculator_c.testserver_on_connection_made
183+ calculator_c = yield self.first_tcp_server.connect_client(
184+ pb.PBClientFactory)
185 calculator = yield calculator_c.getRootObject()
186 adder = yield calculator.callRemote('get_adder')
187 result = yield adder.callRemote('add', first_number, second_number)
188@@ -375,20 +406,20 @@
189 first_number = 1
190 second_number = 2
191 # first server
192- self.first_tcp_server.listen_server(pb.PBServerFactory,
193+ yield self.first_tcp_server.listen_server(pb.PBServerFactory,
194 self.calculator)
195 self.addCleanup(self.first_tcp_server.clean_up)
196
197 # second server
198- self.second_tcp_server.listen_server(pb.PBServerFactory, self.echoer)
199+ yield self.second_tcp_server.listen_server(pb.PBServerFactory,
200+ self.echoer)
201 self.addCleanup(self.second_tcp_server.clean_up)
202
203 # connect the diff clients
204- calculator_c = self.first_tcp_server.connect_client(pb.PBClientFactory)
205- echoer_c = self.second_tcp_server.connect_client(pb.PBClientFactory)
206- # ensure we do have connected
207- yield calculator_c.testserver_on_connection_made
208- yield echoer_c.testserver_on_connection_made
209+ calculator_c = yield self.first_tcp_server.connect_client(
210+ pb.PBClientFactory)
211+ echoer_c = yield self.second_tcp_server.connect_client(
212+ pb.PBClientFactory)
213
214 calculator = yield calculator_c.getRootObject()
215 adder = yield calculator.callRemote('get_adder')
216@@ -398,20 +429,31 @@
217 echo = yield echoer.callRemote('say', 'hello')
218 self.assertEqual(self.echoer.remote_say('hello'), echo)
219
220+ @defer.inlineCallbacks
221 def test_no_single_client(self):
222 """Test setting a single server no client."""
223 # start server but do not connect a client
224- self.first_tcp_server.listen_server(pb.PBServerFactory,
225+ yield self.first_tcp_server.listen_server(pb.PBServerFactory,
226 self.calculator)
227 self.addCleanup(self.first_tcp_server.clean_up)
228
229+ @defer.inlineCallbacks
230 def test_no_multiple_clients(self):
231 """Test setting multiple servers no clients."""
232 # first server
233- self.first_tcp_server.listen_server(pb.PBServerFactory,
234+ yield self.first_tcp_server.listen_server(pb.PBServerFactory,
235 self.calculator)
236 self.addCleanup(self.first_tcp_server.clean_up)
237
238 # second server
239 self.second_tcp_server.listen_server(pb.PBServerFactory, self.echoer)
240 self.addCleanup(self.second_tcp_server.clean_up)
241+
242+
243+@skipIfOS('win32', 'Unix domain sockets not supported on windows.')
244+class UnixMultipleServersTestCase(TCPMultipleServersTestCase):
245+ """Ensure that several servers can be ran."""
246+
247+ def get_server(self):
248+ """Return the server to be used to run the tests."""
249+ return TidyUnixServer()
250
251=== renamed file 'ubuntuone/devtools/testcases/txtcpserver.py' => 'ubuntuone/devtools/testcases/txsocketserver.py'
252--- ubuntuone/devtools/testcases/txtcpserver.py 2012-04-13 16:45:15 +0000
253+++ ubuntuone/devtools/testcases/txsocketserver.py 2012-04-26 14:30:44 +0000
254@@ -28,7 +28,11 @@
255
256 """Base test case for twisted servers."""
257
258-from twisted.internet import defer, protocol
259+import os
260+import shutil
261+import tempfile
262+
263+from twisted.internet import defer, endpoints, protocol
264 from twisted.spread import pb
265
266 from ubuntuone.devtools.testcases import BaseTestCase
267@@ -69,13 +73,6 @@
268 class ClientTidyProtocol(cls):
269 """A tidy protocol."""
270
271- def connectionMade(self):
272- """Connection made."""
273- if (self.factory.testserver_on_connection_made is not None
274- and not self.factory.testserver_on_connection_made.called):
275- self.factory.testserver_on_connection_made.callback(self)
276- cls.connectionMade(self)
277-
278 def connectionLost(self, *a):
279 """Connection list."""
280 # pylint: disable=W0212
281@@ -89,7 +86,7 @@
282 return ClientTidyProtocol
283
284
285-class TidyTCPServer(object):
286+class TidySocketServer(object):
287 """Ensure that twisted servers are correctly managed in tests.
288
289 Closing a twisted server is a complicated matter. In order to do so you
290@@ -103,7 +100,6 @@
291 the reactor is left clean by following the pattern described at
292 http://mumak.net/stuff/twisted-disconnect.html
293 """
294-
295 def __init__(self):
296 """Create a new instance."""
297 self.listener = None
298@@ -112,6 +108,15 @@
299 self.connector = None
300 self.client_factory = None
301
302+ def get_server_endpoint(self):
303+ """Return the server endpoint description."""
304+ raise NotImplementedError('To be implemented by child classes.')
305+
306+ def get_client_endpoint(self):
307+ """Return the client endpoint description."""
308+ raise NotImplementedError('To be implemented by child classes.')
309+
310+ @defer.inlineCallbacks
311 def listen_server(self, server_class, *args, **kwargs):
312 """Start a server in a random port."""
313 from twisted.internet import reactor
314@@ -120,9 +125,12 @@
315 self.server_factory.testserver_on_connection_lost = defer.Deferred()
316 self.server_factory.protocol = server_protocol_factory(
317 self.server_factory.protocol)
318- self.listener = reactor.listenTCP(0, self.server_factory)
319- return self.server_factory
320+ endpoint = endpoints.serverFromString(reactor,
321+ self.get_server_endpoint())
322+ self.listener = yield endpoint.listen(self.server_factory)
323+ defer.returnValue(self.server_factory)
324
325+ @defer.inlineCallbacks
326 def connect_client(self, client_class, *args, **kwargs):
327 """Conect a client to a given server."""
328 from twisted.internet import reactor
329@@ -131,18 +139,17 @@
330 raise ValueError('Server Factory was not provided.')
331 if self.listener is None:
332 raise ValueError('%s has not started listening.',
333- self.server_factory)
334+ self.server_factory)
335
336 self.client_factory = client_class(*args, **kwargs)
337 self.client_factory._disconnecting = False
338 self.client_factory.protocol = client_protocol_factory(
339 self.client_factory.protocol)
340- self.client_factory.testserver_on_connection_made = defer.Deferred()
341 self.client_factory.testserver_on_connection_lost = defer.Deferred()
342- self.connector = reactor.connectTCP('localhost',
343- self.listener.getHost().port,
344- self.client_factory)
345- return self.client_factory
346+ endpoint = endpoints.clientFromString(reactor,
347+ self.get_client_endpoint())
348+ self.connector = yield endpoint.connect(self.client_factory)
349+ defer.returnValue(self.client_factory)
350
351 def clean_up(self):
352 """Action to be performed for clean up."""
353@@ -154,8 +161,8 @@
354 # clean client and server
355 self.server_factory._disconnecting = True
356 self.client_factory._disconnecting = True
357+ self.connector.transport.loseConnection()
358 d = defer.maybeDeferred(self.listener.stopListening)
359- self.connector.disconnect()
360 return defer.gatherResults([d,
361 self.client_factory.testserver_on_connection_lost,
362 self.server_factory.testserver_on_connection_lost])
363@@ -165,14 +172,66 @@
364 return defer.maybeDeferred(self.listener.stopListening)
365
366
367-class TCPServerTestCase(BaseTestCase):
368- """Test that uses a single twisted server."""
369+class TidyTCPServer(TidySocketServer):
370+ """A tidy tcp domain sockets server."""
371+
372+ client_endpoint_pattern = 'tcp:host=127.0.0.1:port=%s'
373+ server_endpoint_pattern = 'tcp:0:interface=127.0.0.1'
374+
375+ def get_server_endpoint(self):
376+ """Return the server endpoint description."""
377+ return self.server_endpoint_pattern
378+
379+ def get_client_endpoint(self):
380+ """Return the client endpoint description."""
381+ if self.server_factory is None:
382+ raise ValueError('Server Factory was not provided.')
383+ if self.listener is None:
384+ raise ValueError('%s has not started listening.',
385+ self.server_factory)
386+ return self.client_endpoint_pattern % self.listener.getHost().port
387+
388+
389+class TidyUnixServer(TidySocketServer):
390+ """A tidy unix domain sockets server."""
391+
392+ client_endpoint_pattern = 'unix:path=%s'
393+ server_endpoint_pattern = 'unix:%s'
394+
395+ def __init__(self):
396+ """Create a new instance."""
397+ super(TidyUnixServer, self).__init__()
398+ self.temp_dir = tempfile.mkdtemp()
399+ self.path = os.path.join(self.temp_dir, 'tidy_unix_server')
400+
401+ def get_server_endpoint(self):
402+ """Return the server endpoint description."""
403+ return self.server_endpoint_pattern % self.path
404+
405+ def get_client_endpoint(self):
406+ """Return the client endpoint description."""
407+ return self.client_endpoint_pattern % self.path
408+
409+ def clean_up(self):
410+ """Action to be performed for clean up."""
411+ result = super(TidyUnixServer, self).clean_up()
412+ # remove the dir once we are disconnected
413+ result.addCallback(lambda _: shutil.rmtree(self.temp_dir))
414+ return result
415+
416+
417+class ServerTestCase(BaseTestCase):
418+ """Base test case for tidy servers."""
419
420 @defer.inlineCallbacks
421 def setUp(self):
422 """Set the diff tests."""
423- yield super(TCPServerTestCase, self).setUp()
424- self.server_runner = TidyTCPServer()
425+ yield super(ServerTestCase, self).setUp()
426+
427+ try:
428+ self.server_runner = self.get_server()
429+ except NotImplementedError:
430+ self.server_runner = None
431
432 self.server_factory = None
433 self.client_factory = None
434@@ -183,28 +242,32 @@
435 self.connector = None
436 self.addCleanup(self.tear_down_server_client)
437
438+ def get_server(self):
439+ """Return the server to be used to run the tests."""
440+ raise NotImplementedError('To be implemented by child classes.')
441+
442+ @defer.inlineCallbacks
443 def listen_server(self, server_class, *args, **kwargs):
444 """Listen a server.
445
446 The method takes the server class and the arguments that should be
447 passed to the server constructor.
448 """
449- self.server_factory = self.server_runner.listen_server(server_class,
450- *args, **kwargs)
451+ self.server_factory = yield self.server_runner.listen_server(
452+ server_class, *args, **kwargs)
453 self.server_disconnected = \
454 self.server_factory.testserver_on_connection_lost
455 self.listener = self.server_runner.listener
456
457+ @defer.inlineCallbacks
458 def connect_client(self, client_class, *args, **kwargs):
459 """Connect the client.
460
461 The method takes the client factory class and the arguments that
462 should be passed to the client constructor.
463 """
464- self.client_factory = self.server_runner.connect_client(client_class,
465- *args, **kwargs)
466- self.client_connected = \
467- self.client_factory.testserver_on_connection_made
468+ self.client_factory = yield self.server_runner.connect_client(
469+ client_class, *args, **kwargs)
470 self.client_disconnected = \
471 self.client_factory.testserver_on_connection_lost
472 self.connector = self.server_runner.connector
473@@ -215,15 +278,53 @@
474 return self.server_runner.clean_up()
475
476
477-class PbServerTestCase(TCPServerTestCase):
478+class TCPServerTestCase(ServerTestCase):
479+ """Test that uses a single twisted server."""
480+
481+ def get_server(self):
482+ """Return the server to be used to run the tests."""
483+ return TidyTCPServer()
484+
485+
486+class UnixServerTestCase(ServerTestCase):
487+ """Test that uses a single twisted server."""
488+
489+ def get_server(self):
490+ """Return the server to be used to run the tests."""
491+ return TidyUnixServer()
492+
493+
494+class PbServerTestCase(ServerTestCase):
495 """Test a pb server."""
496
497+ def get_server(self):
498+ """Return the server to be used to run the tests."""
499+ raise NotImplementedError('To be implemented by child classes.')
500+
501+ @defer.inlineCallbacks
502 def listen_server(self, *args, **kwargs):
503 """Listen a pb server."""
504- super(PbServerTestCase, self).listen_server(pb.PBServerFactory,
505+ yield super(PbServerTestCase, self).listen_server(pb.PBServerFactory,
506 *args, **kwargs)
507
508+ @defer.inlineCallbacks
509 def connect_client(self, *args, **kwargs):
510 """Connect a pb client."""
511- super(PbServerTestCase, self).connect_client(pb.PBClientFactory,
512+ yield super(PbServerTestCase, self).connect_client(pb.PBClientFactory,
513 *args, **kwargs)
514+
515+
516+class TCPPbServerTestCase(PbServerTestCase):
517+ """Test a pb server over TCP."""
518+
519+ def get_server(self):
520+ """Return the server to be used to run the tests."""
521+ return TidyTCPServer()
522+
523+
524+class UnixPbServerTestCase(PbServerTestCase):
525+ """Test a pb server over Unix domain sockets."""
526+
527+ def get_server(self):
528+ """Return the server to be used to run the tests."""
529+ return TidyUnixServer()
530
531=== modified file 'ubuntuone/devtools/testing/txwebserver.py'
532--- ubuntuone/devtools/testing/txwebserver.py 2012-04-17 15:53:14 +0000
533+++ ubuntuone/devtools/testing/txwebserver.py 2012-04-26 14:30:44 +0000
534@@ -32,7 +32,7 @@
535 from twisted.protocols.policies import WrappingFactory
536 from twisted.web import server
537
538-from ubuntuone.devtools.testcases.txtcpserver import server_protocol_factory
539+from ubuntuone.devtools.testcases.txsocketserver import server_protocol_factory
540
541 # no init method + twisted common warnings
542 # pylint: disable=W0232, C0103, E1101

Subscribers

People subscribed via source and target branches

to all changes: