Merge lp:~free.ekanayaka/landscape-client/amp-cleanup-6 into lp:~landscape/landscape-client/trunk

Proposed by Free Ekanayaka
Status: Merged
Approved by: Free Ekanayaka
Approved revision: 670
Merged at revision: 666
Proposed branch: lp:~free.ekanayaka/landscape-client/amp-cleanup-6
Merge into: lp:~landscape/landscape-client/trunk
Diff against target: 1005 lines (+180/-163)
21 files modified
landscape/amp.py (+7/-11)
landscape/broker/amp.py (+3/-3)
landscape/broker/tests/test_amp.py (+15/-13)
landscape/broker/tests/test_service.py (+5/-1)
landscape/configuration.py (+2/-1)
landscape/lib/amp.py (+14/-0)
landscape/lib/tests/test_amp.py (+17/-19)
landscape/manager/tests/test_usermanager.py (+1/-1)
landscape/manager/usermanager.py (+2/-4)
landscape/monitor/tests/test_service.py (+0/-8)
landscape/monitor/usermonitor.py (+2/-2)
landscape/package/taskhandler.py (+1/-4)
landscape/package/tests/test_changer.py (+1/-1)
landscape/package/tests/test_taskhandler.py (+6/-9)
landscape/reactor.py (+42/-25)
landscape/service.py (+0/-5)
landscape/tests/test_amp.py (+16/-20)
landscape/tests/test_configuration.py (+42/-22)
landscape/tests/test_service.py (+0/-5)
landscape/tests/test_watchdog.py (+3/-8)
landscape/watchdog.py (+1/-1)
To merge this branch: bzr merge lp:~free.ekanayaka/landscape-client/amp-cleanup-6
Reviewer Review Type Date Requested Status
Chris Glass (community) Approve
Christopher Armstrong (community) Approve
Geoff Teale (community) Approve
Review via email: mp+161643@code.launchpad.net

Commit message

Another episode of the AMP-related cleanup:

- Rename landscape.amp.RemoteComponentConnector to simply ComponentConnector (the fact that it's remote is implied by the fact that you connect to it, and the new name complements the existing ComponentPublisher nicely)

- Drop the landscape.amp.ComponentProtocolClientFactory sub-class and use MethodCallClientFactory directly, tweaking the needed instance attributes with explicit assignements (this is a sort of composition-over-inheritance change)

- Drop landscape.reactor.UnixReactorMixin. The landscape.reactor.FakeReactor class now really fakes the Unix socket transport, by re-using the machinery in landscape.lib.tests.test_amp, instead of delegating the behavior to the real twisted reactor (hence making it not fake at all)

- Update tests that were using FakeReactor.listen_unix/connect_unix (directly or via helpers) to be synchronous. This means essentially using FakeReactor.advance to simulate time progresses and trigger scheduled calls.

- Make landscape.lib.amp.RemoteObject aware of fake transports, so it can flush it transparently. This is a hacky workaround to the fact that twisted's AMP code doesn't like synchronous transports, and without it we'd need to explicitly flush the fake transport buffers explicitly all the times in a test we communicate with a remote component connected via a fake transport.

- Drop landscape.service.LandscapeService.socket instance attribute, as LandscapeService-derived classes now all use the new landscape.amp.ComponentPublisher convenience introduced in former branches, that takes care of that.

Description of the change

Another episode of the AMP-related cleanup:

- Rename landscape.amp.RemoteComponentConnector to simply ComponentConnector (the fact that it's remote is implied by the fact that you connect to it, and the new name complements the existing ComponentPublisher nicely)

- Drop the landscape.amp.ComponentProtocolClientFactory sub-class and use MethodCallClientFactory directly, tweaking the needed instance attributes with explicit assignements (this is a sort of composition-over-inheritance change)

- Drop landscape.reactor.UnixReactorMixin. The landscape.reactor.FakeReactor class now really fakes the Unix socket transport, by re-using the machinery in landscape.lib.tests.test_amp, instead of delegating the behavior to the real twisted reactor (hence making it not fake at all)

- Update tests that were using FakeReactor.listen_unix/connect_unix (directly or via helpers) to be synchronous. This means essentially using FakeReactor.advance to simulate time progresses and trigger scheduled calls.

- Make landscape.lib.amp.RemoteObject aware of fake transports, so it can flush it transparently. This is a hacky workaround to the fact that twisted's AMP code doesn't like synchronous transports, and without it we'd need to explicitly flush the fake transport buffers explicitly all the times in a test we communicate with a remote component connected via a fake transport.

- Drop landscape.service.LandscapeService.socket instance attribute, as LandscapeService-derived classes now all use the new landscape.amp.ComponentPublisher convenience introduced in former branches, that takes care of that.

To post a comment you must log in.
Revision history for this message
Geoff Teale (tealeg) wrote :

+1

The duplication of comments in test_configuration is a bit odd, but I guess you're trying to ensure that point isn't missed.

review: Approve
Revision history for this message
Christopher Armstrong (radix) wrote :

Have you filed a bug about Twisted's AMP not supporting synchronous transports? I'm not really sure what that means, that's why I ask, so I can go check out the explanation on that bug.

[1] The code around the call to self._factory.connection.flush() is unclear to me. It *seems* that this code is only meant to be run in unit tests, not in normal execution, but the conditional (if the factory has a non-None connection) does not scream "testing-only" to me. Or is there a "flush" method on the real connection object that I'm unfamiliar with?

[2] It doesn't seem right that _socket_paths is a class attribute instead of an instance attribute

Everything else looks fine!

review: Approve
Revision history for this message
Chris Glass (tribaal) wrote :

Looks good! Thanks for doing this cleanup.

[1]
landscape/monitor/tests/test_service.py:1: 'ANY' imported but unused
landscape/service.py:1: 'os' imported but unused

review: Approve
Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Hi Chris, thanks for the careful review. I've filed:

http://twistedmatrix.com/trac/ticket/6502

describing the issue.

[1]

You're right, that code is only meant for unit tests. I've renamed MethodCallClientFactory.connection to fake_connection and added a comment:

    # XXX support exposing fake asynchronous connections created by tests, so
    # they can be flushed transparently and emulate a synchronous behavior. See
    # also http://twistedmatrix.com/trac/ticket/6502, once that's fixed this
    # hack can be removed.
    fake_connection = None

The FakeReactor.connect_unix is the place where it gets set for unit-tests:

    connection = FakeConnection(factory.buildProtocol(path),
                                server.buildProtocol(path))
    factory.fake_connection = connection

[2]

Yeah, that's indeed a hack. The problem is that FakeReactor instances for clients and servers need to be different. I added a comment:

    # XXX probably this shouldn't be a class attribute, but we need client-side
    # FakeReactor instaces to be aware of listening sockets created by
    # server-side FakeReactor instances.
    _socket_paths = {}

Maybe the way to solve it would be to add API for declaring a reactor "aware" of another reactor (so tests would need to call it explicitly). Suggestions are welcome.

669. By Free Ekanayaka

Address radix's comments

670. By Free Ekanayaka

Drop unused imports

Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Hey Chris (Glass), unused imports fixed.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'landscape/amp.py'
--- landscape/amp.py 2013-04-29 15:46:13 +0000
+++ landscape/amp.py 2013-05-02 08:30:35 +0000
@@ -17,11 +17,6 @@
17 MethodCallClientFactory, MethodCallServerFactory, RemoteObject)17 MethodCallClientFactory, MethodCallServerFactory, RemoteObject)
1818
1919
20class ComponentProtocolClientFactory(MethodCallClientFactory):
21
22 initialDelay = 0.05
23
24
25class ComponentPublisher(object):20class ComponentPublisher(object):
26 """Publish a Landscape client component using a UNIX socket.21 """Publish a Landscape client component using a UNIX socket.
2722
@@ -35,6 +30,7 @@
35 """30 """
3631
37 methods = ("ping", "exit")32 methods = ("ping", "exit")
33 factory = MethodCallServerFactory
3834
39 def __init__(self, component, reactor, config):35 def __init__(self, component, reactor, config):
40 self._reactor = reactor36 self._reactor = reactor
@@ -53,7 +49,7 @@
53 return self._port.stopListening()49 return self._port.stopListening()
5450
5551
56class RemoteComponentConnector(object):52class ComponentConnector(object):
57 """Utility superclass for creating connections with a Landscape component.53 """Utility superclass for creating connections with a Landscape component.
5854
59 @cvar component: The class of the component to connect to, it is expected55 @cvar component: The class of the component to connect to, it is expected
@@ -71,8 +67,8 @@
7167
72 @see: L{MethodCallClientFactory}.68 @see: L{MethodCallClientFactory}.
73 """69 """
70 factory = MethodCallClientFactory
74 component = None # Must be defined by sub-classes71 component = None # Must be defined by sub-classes
75 factory = ComponentProtocolClientFactory
76 remote = RemoteObject72 remote = RemoteObject
7773
78 def __init__(self, reactor, config, retry_on_reconnect=False):74 def __init__(self, reactor, config, retry_on_reconnect=False):
@@ -95,8 +91,8 @@
95 result in a faster reconnection attempts pace.91 result in a faster reconnection attempts pace.
96 @param quiet: A boolean indicating whether to log errors.92 @param quiet: A boolean indicating whether to log errors.
97 """93 """
98 reactor = self._reactor._reactor94 factory = self.factory(self._reactor._reactor)
99 factory = self.factory(reactor)95 factory.initialDelay = factory.delay = 0.05
100 factory.retryOnReconnect = self._retry_on_reconnect96 factory.retryOnReconnect = self._retry_on_reconnect
101 factory.remote = self.remote97 factory.remote = self.remote
10298
@@ -115,8 +111,8 @@
115 if factor:111 if factor:
116 factory.factor = factor112 factory.factor = factor
117 socket_path = _get_socket_path(self.component, self._config)113 socket_path = _get_socket_path(self.component, self._config)
118 self._connector = reactor.connectUNIX(socket_path, factory)
119 deferred = factory.getRemoteObject()114 deferred = factory.getRemoteObject()
115 self._connector = self._reactor.connect_unix(socket_path, factory)
120116
121 if not quiet:117 if not quiet:
122 deferred.addErrback(log_error)118 deferred.addErrback(log_error)
@@ -155,7 +151,7 @@
155 def register(cls, connector_class):151 def register(cls, connector_class):
156 """Register a connector for a Landscape component.152 """Register a connector for a Landscape component.
157153
158 @param connector_class: A sub-class of L{RemoteComponentConnector}154 @param connector_class: A sub-class of L{ComponentConnector}
159 that can be used to connect to a certain component.155 that can be used to connect to a certain component.
160 """156 """
161 cls._by_name[connector_class.component.name] = connector_class157 cls._by_name[connector_class.component.name] = connector_class
162158
=== modified file 'landscape/broker/amp.py'
--- landscape/broker/amp.py 2013-04-29 11:44:32 +0000
+++ landscape/broker/amp.py 2013-05-02 08:30:35 +0000
@@ -2,7 +2,7 @@
22
3from landscape.lib.amp import RemoteObject, MethodCallArgument3from landscape.lib.amp import RemoteObject, MethodCallArgument
4from landscape.amp import (4from landscape.amp import (
5 RemoteComponentConnector, RemoteComponentsRegistry, ComponentPublisher)5 ComponentConnector, RemoteComponentsRegistry, ComponentPublisher)
6from landscape.broker.server import BrokerServer6from landscape.broker.server import BrokerServer
7from landscape.broker.client import BrokerClient7from landscape.broker.client import BrokerClient
8from landscape.monitor.monitor import Monitor8from landscape.monitor.monitor import Monitor
@@ -95,14 +95,14 @@
95 "message")95 "message")
9696
9797
98class RemoteBrokerConnector(RemoteComponentConnector):98class RemoteBrokerConnector(ComponentConnector):
99 """Helper to create connections with the L{BrokerServer}."""99 """Helper to create connections with the L{BrokerServer}."""
100100
101 remote = RemoteBroker101 remote = RemoteBroker
102 component = BrokerServer102 component = BrokerServer
103103
104104
105class RemoteClientConnector(RemoteComponentConnector):105class RemoteClientConnector(ComponentConnector):
106 """Helper to create connections with the L{BrokerServer}."""106 """Helper to create connections with the L{BrokerServer}."""
107107
108 component = BrokerClient108 component = BrokerClient
109109
=== modified file 'landscape/broker/tests/test_amp.py'
--- landscape/broker/tests/test_amp.py 2013-04-15 09:03:43 +0000
+++ landscape/broker/tests/test_amp.py 2013-05-02 08:30:35 +0000
@@ -168,9 +168,11 @@
168 L{RemoteBroker.listen_events} returns a deferred which fires when168 L{RemoteBroker.listen_events} returns a deferred which fires when
169 the first of the given events occurs in the broker reactor.169 the first of the given events occurs in the broker reactor.
170 """170 """
171 result = self.remote.listen_events(["event1", "event2"])171 deferred = self.remote.listen_events(["event1", "event2"])
172 self.reactor._reactor.callLater(0.05, self.reactor.fire, "event2")172 self.reactor.call_later(0.05, self.reactor.fire, "event2")
173 return self.assertSuccess(result, "event2")173 self.reactor.advance(0.05)
174 self.remote._factory.fake_connection.flush()
175 self.assertEqual("event2", self.successResultOf(deferred))
174176
175 def test_call_on_events(self):177 def test_call_on_events(self):
176 """178 """
@@ -182,10 +184,12 @@
182 callback2 = self.mocker.mock()184 callback2 = self.mocker.mock()
183 self.expect(callback2()).result(123)185 self.expect(callback2()).result(123)
184 self.mocker.replay()186 self.mocker.replay()
185 result = self.remote.call_on_event({"event1": callback1,187 deferred = self.remote.call_on_event({"event1": callback1,
186 "event2": callback2})188 "event2": callback2})
187 self.reactor._reactor.callLater(0.05, self.reactor.fire, "event2")189 self.reactor.call_later(0.05, self.reactor.fire, "event2")
188 return self.assertSuccess(result, 123)190 self.reactor.advance(0.05)
191 self.remote._factory.fake_connection.flush()
192 self.assertEqual(123, self.successResultOf(deferred))
189193
190 def test_fire_event(self):194 def test_fire_event(self):
191 """195 """
@@ -202,9 +206,8 @@
202 """206 """
203 Trying to call an non-exposed broker method results in a failure.207 Trying to call an non-exposed broker method results in a failure.
204 """208 """
205 result = self.remote._sender.send_method_call(209 deferred = self.remote.get_clients()
206 method="get_clients", args=[], kwargs={})210 self.failureResultOf(deferred).trap(MethodCallError)
207 return self.assertFailure(result, MethodCallError)
208211
209212
210class RemoteClientTest(LandscapeTest):213class RemoteClientTest(LandscapeTest):
@@ -263,6 +266,5 @@
263 """266 """
264 Trying to call an non-exposed client method results in a failure.267 Trying to call an non-exposed client method results in a failure.
265 """268 """
266 result = self.remote_client._sender.send_method_call(269 deferred = self.remote_client.get_plugins()
267 method="get_plugins", args=[], kwargs={})270 self.failureResultOf(deferred).trap(MethodCallError)
268 return self.assertFailure(result, MethodCallError)
269271
=== modified file 'landscape/broker/tests/test_service.py'
--- landscape/broker/tests/test_service.py 2013-04-29 11:37:27 +0000
+++ landscape/broker/tests/test_service.py 2013-05-02 08:30:35 +0000
@@ -14,7 +14,11 @@
1414
15 def setUp(self):15 def setUp(self):
16 super(BrokerServiceTest, self).setUp()16 super(BrokerServiceTest, self).setUp()
17 self.service = BrokerService(self.config)17
18 class FakeBrokerService(BrokerService):
19 reactor_factory = FakeReactor
20
21 self.service = FakeBrokerService(self.config)
1822
19 def test_persist(self):23 def test_persist(self):
20 """24 """
2125
=== modified file 'landscape/configuration.py'
--- landscape/configuration.py 2013-03-27 22:58:20 +0000
+++ landscape/configuration.py 2013-05-02 08:30:35 +0000
@@ -618,7 +618,8 @@
618618
619 0.05 * (1 - 1.62 ** 14) / (1 - 1.62) = 69 seconds619 0.05 * (1 - 1.62 ** 14) / (1 - 1.62) = 69 seconds
620 """620 """
621 reactor = TwistedReactor()621 if reactor is None:
622 reactor = TwistedReactor()
622 exit_with_error = []623 exit_with_error = []
623624
624 def stop(error=None):625 def stop(error=None):
625626
=== modified file 'landscape/lib/amp.py'
--- landscape/lib/amp.py 2013-04-30 16:21:01 +0000
+++ landscape/lib/amp.py 2013-05-02 08:30:35 +0000
@@ -349,6 +349,14 @@
349 result.addCallback(self._handle_result, deferred)349 result.addCallback(self._handle_result, deferred)
350 result.addErrback(self._handle_failure, method, args, kwargs,350 result.addErrback(self._handle_failure, method, args, kwargs,
351 deferred)351 deferred)
352
353 if self._factory.fake_connection is not None:
354 # Transparently flush the connection after a send_method_call
355 # invokation letting tests simulate a synchronous transport.
356 # This is needed because the Twisted's AMP implementation
357 # assume that the transport is asynchronous.
358 self._factory.fake_connection.flush()
359
352 return deferred360 return deferred
353361
354 return send_method_call362 return send_method_call
@@ -487,6 +495,12 @@
487 retryOnReconnect = False495 retryOnReconnect = False
488 retryTimeout = None496 retryTimeout = None
489497
498 # XXX support exposing fake asynchronous connections created by tests, so
499 # they can be flushed transparently and emulate a synchronous behavior. See
500 # also http://twistedmatrix.com/trac/ticket/6502, once that's fixed this
501 # hack can be removed.
502 fake_connection = None
503
490 def __init__(self, reactor=None):504 def __init__(self, reactor=None):
491 """505 """
492 @param object: The object exposed by the L{MethodCallProtocol}s506 @param object: The object exposed by the L{MethodCallProtocol}s
493507
=== modified file 'landscape/lib/tests/test_amp.py'
--- landscape/lib/tests/test_amp.py 2013-04-30 16:21:01 +0000
+++ landscape/lib/tests/test_amp.py 2013-05-02 08:30:35 +0000
@@ -1,28 +1,28 @@
1from twisted.internet import reactor1from twisted.internet import reactor
2from twisted.internet.error import ConnectError, ConnectionDone
3from twisted.internet.task import Clock
2from twisted.internet.defer import Deferred, DeferredList4from twisted.internet.defer import Deferred, DeferredList
3from twisted.internet.error import ConnectionDone, ConnectError
4from twisted.internet.task import Clock
5from twisted.protocols.amp import AMP
6from twisted.python.failure import Failure5from twisted.python.failure import Failure
76
8from landscape.lib.amp import (7from landscape.lib.amp import (
9 MethodCallError, MethodCallServerProtocol, MethodCallClientProtocol,8 MethodCallError, MethodCallServerProtocol, MethodCallClientProtocol,
10 MethodCallServerFactory, MethodCallClientFactory, RemoteObject,9 MethodCallServerFactory, MethodCallClientFactory, RemoteObject,
11 RemoteObjectConnector, MethodCallReceiver, MethodCallSender)10 RemoteObjectConnector, MethodCallSender)
12from landscape.tests.helpers import LandscapeTest11from landscape.tests.helpers import LandscapeTest
1312
1413
15class FakeTransport(object):14class FakeTransport(object):
16 """Accumulate written data into a list."""15 """Accumulate written data into a list."""
1716
18 def __init__(self):17 def __init__(self, connection):
19 self.stream = []18 self.stream = []
19 self.connection = connection
2020
21 def write(self, data):21 def write(self, data):
22 self.stream.append(data)22 self.stream.append(data)
2323
24 def loseConnection(self):24 def loseConnection(self):
25 raise NotImplemented()25 self.connection.disconnect()
2626
27 def getPeer(self):27 def getPeer(self):
28 pass28 pass
@@ -37,8 +37,16 @@
37 def __init__(self, client, server):37 def __init__(self, client, server):
38 self.client = client38 self.client = client
39 self.server = server39 self.server = server
40 self.server.makeConnection(FakeTransport())40 self.connect()
41 self.client.makeConnection(FakeTransport())41
42 def connect(self):
43 self.server.makeConnection(FakeTransport(self))
44 self.client.makeConnection(FakeTransport(self))
45
46 def disconnect(self):
47 reason = Failure(ConnectionDone())
48 connector = self
49 self.client.factory.clientConnectionLost(connector, reason)
4250
43 def flush(self):51 def flush(self):
44 """52 """
@@ -338,17 +346,7 @@
338 client.factory = WordsFactory(self.clock)346 client.factory = WordsFactory(self.clock)
339 self.remote = RemoteObject(client.factory)347 self.remote = RemoteObject(client.factory)
340 self.connection = FakeConnection(client, server)348 self.connection = FakeConnection(client, server)
341349 client.factory.fake_connection = self.connection
342 send_method_call = self.remote._sender.send_method_call
343
344 def synchronous_send_method_call(method, args=[], kwargs={}):
345 # Transparently flush the connection after a send_method_call
346 # invocation
347 deferred = send_method_call(method, args=args, kwargs=kwargs)
348 self.connection.flush()
349 return deferred
350
351 self.remote._sender.send_method_call = synchronous_send_method_call
352350
353 def test_method_call_sender_with_forbidden_method(self):351 def test_method_call_sender_with_forbidden_method(self):
354 """352 """
355353
=== modified file 'landscape/manager/tests/test_usermanager.py'
--- landscape/manager/tests/test_usermanager.py 2013-03-20 08:58:22 +0000
+++ landscape/manager/tests/test_usermanager.py 2013-05-02 08:30:35 +0000
@@ -422,7 +422,7 @@
422 def handle_callback(ignored):422 def handle_callback(ignored):
423 messages = self.broker_service.message_store.get_pending_messages()423 messages = self.broker_service.message_store.get_pending_messages()
424 # Ignore the message created by plugin.run.424 # Ignore the message created by plugin.run.
425 messages = sorted(messages[1:3],425 messages = sorted([messages[1], messages[3]],
426 key=lambda message: message["operation-id"])426 key=lambda message: message["operation-id"])
427 self.assertMessages(messages,427 self.assertMessages(messages,
428 [{"type": "operation-result",428 [{"type": "operation-result",
429429
=== modified file 'landscape/manager/usermanager.py'
--- landscape/manager/usermanager.py 2013-04-22 09:32:25 +0000
+++ landscape/manager/usermanager.py 2013-05-02 08:30:35 +0000
@@ -1,9 +1,7 @@
1import os
2import logging1import logging
32
4from landscape.lib.amp import RemoteObject
5from landscape.lib.encoding import encode_dict_if_needed3from landscape.lib.encoding import encode_dict_if_needed
6from landscape.amp import ComponentPublisher, RemoteComponentConnector4from landscape.amp import ComponentPublisher, ComponentConnector
75
8from landscape.user.management import UserManagement6from landscape.user.management import UserManagement
9from landscape.manager.plugin import ManagerPlugin7from landscape.manager.plugin import ManagerPlugin
@@ -158,6 +156,6 @@
158 methods = ["get_locked_usernames"]156 methods = ["get_locked_usernames"]
159157
160158
161class RemoteUserManagerConnector(RemoteComponentConnector):159class RemoteUserManagerConnector(ComponentConnector):
162160
163 component = UserManager161 component = UserManager
164162
=== modified file 'landscape/monitor/tests/test_service.py'
--- landscape/monitor/tests/test_service.py 2013-04-29 11:37:27 +0000
+++ landscape/monitor/tests/test_service.py 2013-05-02 08:30:35 +0000
@@ -1,4 +1,3 @@
1from landscape.tests.mocker import ANY
2from landscape.tests.helpers import LandscapeTest, FakeBrokerServiceHelper1from landscape.tests.helpers import LandscapeTest, FakeBrokerServiceHelper
3from landscape.reactor import FakeReactor2from landscape.reactor import FakeReactor
4from landscape.monitor.config import MonitorConfiguration, ALL_PLUGINS3from landscape.monitor.config import MonitorConfiguration, ALL_PLUGINS
@@ -46,13 +45,6 @@
46 starts the plugins and register the monitor as broker client. It also45 starts the plugins and register the monitor as broker client. It also
47 start listening on its own socket for incoming connections.46 start listening on its own socket for incoming connections.
48 """47 """
49 # FIXME: don't actually run the real register method, because at the
50 # moment the UserMonitor plugin still depends on DBus. We can probably
51 # drop this mocking once the AMP migration is completed.
52 for plugin in self.service.plugins:
53 plugin.register = self.mocker.mock()
54 plugin.register(ANY)
55 self.mocker.replay()
5648
57 def stop_service(ignored):49 def stop_service(ignored):
58 [connector] = self.broker_service.broker.get_connectors()50 [connector] = self.broker_service.broker.get_connectors()
5951
=== modified file 'landscape/monitor/usermonitor.py'
--- landscape/monitor/usermonitor.py 2013-04-22 09:32:25 +0000
+++ landscape/monitor/usermonitor.py 2013-05-02 08:30:35 +0000
@@ -1,7 +1,7 @@
1from twisted.internet.defer import maybeDeferred1from twisted.internet.defer import maybeDeferred
22
3from landscape.lib.log import log_failure3from landscape.lib.log import log_failure
4from landscape.amp import ComponentPublisher, RemoteComponentConnector4from landscape.amp import ComponentPublisher, ComponentConnector
55
6from landscape.monitor.plugin import MonitorPlugin6from landscape.monitor.plugin import MonitorPlugin
7from landscape.user.changes import UserChanges7from landscape.user.changes import UserChanges
@@ -111,6 +111,6 @@
111 methods = ["detect_changes"]111 methods = ["detect_changes"]
112112
113113
114class RemoteUserMonitorConnector(RemoteComponentConnector):114class RemoteUserMonitorConnector(ComponentConnector):
115115
116 component = UserMonitor116 component = UserMonitor
117117
=== modified file 'landscape/package/taskhandler.py'
--- landscape/package/taskhandler.py 2013-02-28 10:09:09 +0000
+++ landscape/package/taskhandler.py 2013-05-02 08:30:35 +0000
@@ -276,10 +276,7 @@
276276
277 def finish():277 def finish():
278 connector.disconnect()278 connector.disconnect()
279 # For some obscure reason our TwistedReactor.stop method calls279 reactor.call_later(0, reactor.stop)
280 # reactor.crash() instead of reactor.stop(), which doesn't work
281 # here. Maybe TwistedReactor.stop should simply use reactor.stop().
282 reactor.call_later(0, reactor._reactor.stop)
283280
284 def got_error(failure):281 def got_error(failure):
285 log_failure(failure)282 log_failure(failure)
286283
=== modified file 'landscape/package/tests/test_changer.py'
--- landscape/package/tests/test_changer.py 2013-03-18 03:34:11 +0000
+++ landscape/package/tests/test_changer.py 2013-05-02 08:30:35 +0000
@@ -1479,7 +1479,7 @@
1479 self.broker_service.reactor.advance(100)1479 self.broker_service.reactor.advance(100)
1480 self.twisted_reactor.advance(10)1480 self.twisted_reactor.advance(10)
1481 payloads = self.broker_service.exchanger._transport.payloads1481 payloads = self.broker_service.exchanger._transport.payloads
1482 self.assertEqual(1, len(payloads))1482 self.assertEqual(0, len(payloads))
14831483
1484 [arguments] = self.process_factory.spawns1484 [arguments] = self.process_factory.spawns
1485 protocol = arguments[0]1485 protocol = arguments[0]
14861486
=== modified file 'landscape/package/tests/test_taskhandler.py'
--- landscape/package/tests/test_taskhandler.py 2012-05-10 00:34:13 +0000
+++ landscape/package/tests/test_taskhandler.py 2013-05-02 08:30:35 +0000
@@ -1,10 +1,9 @@
1import os1import os
22
3from twisted.internet import reactor
4from twisted.internet.defer import Deferred, fail3from twisted.internet.defer import Deferred, fail
54
6from landscape.lib.lock import lock_path5from landscape.lib.lock import lock_path
7from landscape.reactor import TwistedReactor6from landscape.reactor import TwistedReactor, FakeReactor
8from landscape.broker.amp import RemoteBrokerConnector7from landscape.broker.amp import RemoteBrokerConnector
9from landscape.package.taskhandler import (8from landscape.package.taskhandler import (
10 PackageTaskHandlerConfiguration, PackageTaskHandler, run_task_handler,9 PackageTaskHandlerConfiguration, PackageTaskHandler, run_task_handler,
@@ -366,7 +365,8 @@
366 reactor_mock.run()365 reactor_mock.run()
367 self.mocker.call(lambda: call_when_running[0]())366 self.mocker.call(lambda: call_when_running[0]())
368 connector_mock.disconnect()367 connector_mock.disconnect()
369 reactor_mock.call_later(0, reactor.stop)368 reactor_mock.call_later(0, ANY)
369 self.mocker.result(None)
370370
371 # Okay, the whole playground is set.371 # Okay, the whole playground is set.
372 self.mocker.replay()372 self.mocker.replay()
@@ -433,12 +433,10 @@
433433
434 def test_errors_in_tasks_are_printed_and_exit_program(self):434 def test_errors_in_tasks_are_printed_and_exit_program(self):
435 # Ignore a bunch of crap that we don't care about435 # Ignore a bunch of crap that we don't care about
436 reactor_mock = self.mocker.patch(TwistedReactor)
437 init_logging_mock = self.mocker.replace("landscape.deployment"436 init_logging_mock = self.mocker.replace("landscape.deployment"
438 ".init_logging",437 ".init_logging",
439 passthrough=False)438 passthrough=False)
440 init_logging_mock(ARGS)439 init_logging_mock(ARGS)
441 reactor_mock.run()
442440
443 class MyException(Exception):441 class MyException(Exception):
444 pass442 pass
@@ -450,8 +448,6 @@
450 handler_mock = handler_factory_mock(ARGS)448 handler_mock = handler_factory_mock(ARGS)
451 self.expect(handler_mock.run()).result(fail(MyException("Hey error")))449 self.expect(handler_mock.run()).result(fail(MyException("Hey error")))
452450
453 reactor_mock.call_later(0, reactor.stop)
454
455 self.mocker.replay()451 self.mocker.replay()
456452
457 # Ok now for some real stuff453 # Ok now for some real stuff
@@ -460,7 +456,8 @@
460 self.assertIn("MyException", self.logfile.getvalue())456 self.assertIn("MyException", self.logfile.getvalue())
461457
462 result = run_task_handler(handler_factory_mock,458 result = run_task_handler(handler_factory_mock,
463 ["-c", self.config_filename])459 ["-c", self.config_filename],
460 reactor=FakeReactor())
464 return result.addCallback(assert_log)461 return result.addCallback(assert_log)
465462
466463
@@ -473,7 +470,7 @@
473 The L{LazyRemoteBroker} class doesn't initialize the actual remote470 The L{LazyRemoteBroker} class doesn't initialize the actual remote
474 broker until one of its attributes gets actually accessed.471 broker until one of its attributes gets actually accessed.
475 """472 """
476 reactor = TwistedReactor()473 reactor = FakeReactor()
477 connector = RemoteBrokerConnector(reactor, self.broker_service.config)474 connector = RemoteBrokerConnector(reactor, self.broker_service.config)
478 self.broker = LazyRemoteBroker(connector)475 self.broker = LazyRemoteBroker(connector)
479 self.assertIs(self.broker._remote, None)476 self.assertIs(self.broker._remote, None)
480477
=== modified file 'landscape/reactor.py'
--- landscape/reactor.py 2013-04-12 11:49:59 +0000
+++ landscape/reactor.py 2013-05-02 08:30:35 +0000
@@ -6,6 +6,8 @@
6import logging6import logging
7import bisect7import bisect
88
9from twisted.python.failure import Failure
10from twisted.internet.error import ConnectError
9from twisted.internet.threads import deferToThread11from twisted.internet.threads import deferToThread
1012
11from landscape.log import format_object13from landscape.log import format_object
@@ -110,33 +112,13 @@
110 raise InvalidID("EventID instance expected, received %r" % id)112 raise InvalidID("EventID instance expected, received %r" % id)
111113
112114
113class UnixReactorMixin(object):
114 """Support listening on Unix domain sockets.
115
116 Note that this mixin uses the *real* Twisted reactor to open a *real*
117 socket.
118
119 Since it is used by *both* L{TwistedReactor} and L{FakeReactor}, this
120 means that the latter is not really fake in this sense and that unit
121 tests involving calls to this method won't be synchronous anymore.
122
123 For example, many tests under L{landscape.broker.tests} use C{listen_unix}
124 to setup a "real" remote broker and exercise the RPC/AMP functionality. See
125 in particular L{landscape.broker.tests.helpers.RemoteBrokerHelper}.
126 """
127
128 def listen_unix(self, socket, factory):
129 """Start listening on a Unix socket."""
130 return self._reactor.listenUNIX(socket, factory, wantPID=True)
131
132
133class ReactorID(object):115class ReactorID(object):
134116
135 def __init__(self, timeout):117 def __init__(self, timeout):
136 self._timeout = timeout118 self._timeout = timeout
137119
138120
139class TwistedReactor(EventHandlingReactorMixin, UnixReactorMixin):121class TwistedReactor(EventHandlingReactorMixin):
140 """Wrap and add functionalities to the Twisted C{reactor}.122 """Wrap and add functionalities to the Twisted C{reactor}.
141123
142 This essentially a facade around the C{twisted.internet.reactor} and124 This essentially a facade around the C{twisted.internet.reactor} and
@@ -239,6 +221,14 @@
239 deferred.addCallback(on_success)221 deferred.addCallback(on_success)
240 deferred.addErrback(on_failure)222 deferred.addErrback(on_failure)
241223
224 def listen_unix(self, socket, factory):
225 """Start listening on a Unix socket."""
226 return self._reactor.listenUNIX(socket, factory, wantPID=True)
227
228 def connect_unix(self, socket, factory):
229 """Connect to a Unix socket."""
230 return self._reactor.connectUNIX(socket, factory)
231
242 def run(self):232 def run(self):
243 """Start the reactor, a C{"run"} event will be fired."""233 """Start the reactor, a C{"run"} event will be fired."""
244234
@@ -266,7 +256,7 @@
266 self._data = data256 self._data = data
267257
268258
269class FakeReactor(EventHandlingReactorMixin, UnixReactorMixin):259class FakeReactor(EventHandlingReactorMixin):
270 """A fake reactor with the same API of L{TwistedReactor}.260 """A fake reactor with the same API of L{TwistedReactor}.
271261
272 This reactor emulates the asychronous interface of L{TwistedReactor}, but262 This reactor emulates the asychronous interface of L{TwistedReactor}, but
@@ -279,6 +269,10 @@
279 around Unix sockets), and implement a fake version C{listen_unix}, but this269 around Unix sockets), and implement a fake version C{listen_unix}, but this
280 hasn't been done yet.270 hasn't been done yet.
281 """271 """
272 # XXX probably this shouldn't be a class attribute, but we need client-side
273 # FakeReactor instaces to be aware of listening sockets created by
274 # server-side FakeReactor instances.
275 _socket_paths = {}
282276
283 def __init__(self):277 def __init__(self):
284 super(FakeReactor, self).__init__()278 super(FakeReactor, self).__init__()
@@ -287,8 +281,8 @@
287 self.hosts = {}281 self.hosts = {}
288 self._threaded_callbacks = []282 self._threaded_callbacks = []
289283
290 # We need a reference to the Twisted reactor as well to284 # XXX we need a reference to the Twisted reactor as well because
291 # let Landscape services listen to Unix sockets285 # some tests use it
292 from twisted.internet import reactor286 from twisted.internet import reactor
293 self._reactor = reactor287 self._reactor = reactor
294288
@@ -326,7 +320,8 @@
326 super(FakeReactor, self).cancel_call(id)320 super(FakeReactor, self).cancel_call(id)
327321
328 def call_when_running(self, f):322 def call_when_running(self, f):
329 raise NotImplemented("The FakeReactor doesn't implement this.")323 # Just schedule a call that will be kicked by the run() method.
324 self.call_later(0, f)
330325
331 def call_in_main(self, f, *args, **kwargs):326 def call_in_main(self, f, *args, **kwargs):
332 """Schedule a function for execution in the main thread."""327 """Schedule a function for execution in the main thread."""
@@ -347,6 +342,28 @@
347 self._in_thread(callback, errback, f, args, kwargs)342 self._in_thread(callback, errback, f, args, kwargs)
348 self._run_threaded_callbacks()343 self._run_threaded_callbacks()
349344
345 def listen_unix(self, socket_path, factory):
346
347 class FakePort(object):
348
349 def stopListening(oself):
350 self._socket_paths.pop(socket_path)
351
352 self._socket_paths[socket_path] = factory
353 return FakePort()
354
355 def connect_unix(self, path, factory):
356 server = self._socket_paths.get(path)
357 from landscape.lib.tests.test_amp import FakeConnection
358 if server:
359 connection = FakeConnection(factory.buildProtocol(path),
360 server.buildProtocol(path))
361 factory.fake_connection = connection
362 else:
363 connector = object() # Fake connector
364 failure = Failure(ConnectError("No such file or directory"))
365 factory.clientConnectionFailed(connector, failure)
366
350 def run(self):367 def run(self):
351 """Continuously advance this reactor until reactor.stop() is called."""368 """Continuously advance this reactor until reactor.stop() is called."""
352 self.fire("run")369 self.fire("run")
353370
=== modified file 'landscape/service.py'
--- landscape/service.py 2013-04-29 11:44:32 +0000
+++ landscape/service.py 2013-05-02 08:30:35 +0000
@@ -1,4 +1,3 @@
1import os
2import logging1import logging
3import signal2import signal
43
@@ -42,15 +41,11 @@
42 signal.signal(41 signal.signal(
43 signal.SIGUSR1,42 signal.SIGUSR1,
44 lambda signal, frame: reactor.callFromThread(rotate_logs))43 lambda signal, frame: reactor.callFromThread(rotate_logs))
45 self.socket = os.path.join(self.config.sockets_path,
46 self.service_name + ".sock")
4744
48 def startService(self):45 def startService(self):
49 Service.startService(self)46 Service.startService(self)
50 logging.info("%s started with config %s" % (47 logging.info("%s started with config %s" % (
51 self.service_name.capitalize(), self.config.get_config_filename()))48 self.service_name.capitalize(), self.config.get_config_filename()))
52 if hasattr(self, "factory"):
53 self.port = self.reactor.listen_unix(self.socket, self.factory)
5449
55 def stopService(self):50 def stopService(self):
56 # We don't need to call port.stopListening(), because the reactor51 # We don't need to call port.stopListening(), because the reactor
5752
=== modified file 'landscape/tests/test_amp.py'
--- landscape/tests/test_amp.py 2013-04-22 09:32:25 +0000
+++ landscape/tests/test_amp.py 2013-05-02 08:30:35 +0000
@@ -4,9 +4,7 @@
4from landscape.tests.helpers import LandscapeTest4from landscape.tests.helpers import LandscapeTest
5from landscape.reactor import FakeReactor5from landscape.reactor import FakeReactor
6from landscape.deployment import Configuration6from landscape.deployment import Configuration
7from landscape.amp import (7from landscape.amp import ComponentPublisher, ComponentConnector
8 ComponentProtocolClientFactory, RemoteComponentConnector,
9 ComponentPublisher)
108
119
12class TestComponent(object):10class TestComponent(object):
@@ -14,22 +12,21 @@
14 name = "test"12 name = "test"
1513
1614
17class TestComponentProtocolFactory(ComponentProtocolClientFactory):15class TestComponentConnector(ComponentConnector):
1816
19 maxRetries = 0
20 initialDelay = 0.01
21
22
23class RemoteTestComponentConnector(RemoteComponentConnector):
24
25 factory = TestComponentProtocolFactory
26 component = TestComponent17 component = TestComponent
2718
2819
29class RemoteComponentTest(LandscapeTest):20class FakeAMP(object):
21
22 def __init__(self, locator):
23 self._locator = locator
24
25
26class ComponentPublisherTest(LandscapeTest):
3027
31 def setUp(self):28 def setUp(self):
32 super(RemoteComponentTest, self).setUp()29 super(ComponentPublisherTest, self).setUp()
33 reactor = FakeReactor()30 reactor = FakeReactor()
34 config = Configuration()31 config = Configuration()
35 config.data_path = self.makeDir()32 config.data_path = self.makeDir()
@@ -38,7 +35,7 @@
38 self.publisher = ComponentPublisher(self.component, reactor, config)35 self.publisher = ComponentPublisher(self.component, reactor, config)
39 self.publisher.start()36 self.publisher.start()
4037
41 self.connector = RemoteTestComponentConnector(reactor, config)38 self.connector = TestComponentConnector(reactor, config)
42 connected = self.connector.connect()39 connected = self.connector.connect()
43 connected.addCallback(lambda remote: setattr(self, "remote", remote))40 connected.addCallback(lambda remote: setattr(self, "remote", remote))
44 return connected41 return connected
@@ -46,7 +43,7 @@
46 def tearDown(self):43 def tearDown(self):
47 self.connector.disconnect()44 self.connector.disconnect()
48 self.publisher.stop()45 self.publisher.stop()
49 super(RemoteComponentTest, self).tearDown()46 super(ComponentPublisherTest, self).tearDown()
5047
51 def test_ping(self):48 def test_ping(self):
52 """49 """
@@ -71,16 +68,15 @@
71 return self.assertSuccess(result)68 return self.assertSuccess(result)
7269
7370
74class RemoteComponentConnectorTest(LandscapeTest):71class ComponentConnectorTest(LandscapeTest):
7572
76 def setUp(self):73 def setUp(self):
77 super(RemoteComponentConnectorTest, self).setUp()74 super(ComponentConnectorTest, self).setUp()
78 self.reactor = FakeReactor()75 self.reactor = FakeReactor()
79 self.config = Configuration()76 self.config = Configuration()
80 self.config.data_path = self.makeDir()77 self.config.data_path = self.makeDir()
81 self.makeDir(path=self.config.sockets_path)78 self.makeDir(path=self.config.sockets_path)
82 self.connector = RemoteTestComponentConnector(self.reactor,79 self.connector = TestComponentConnector(self.reactor, self.config)
83 self.config)
8480
85 def test_connect_logs_errors(self):81 def test_connect_logs_errors(self):
86 """82 """
8783
=== modified file 'landscape/tests/test_configuration.py'
--- landscape/tests/test_configuration.py 2013-04-18 09:32:04 +0000
+++ landscape/tests/test_configuration.py 2013-05-02 08:30:35 +0000
@@ -5,9 +5,10 @@
5from cStringIO import StringIO5from cStringIO import StringIO
66
7from twisted.internet.defer import succeed, fail7from twisted.internet.defer import succeed, fail
8from twisted.internet.task import Clock
89
9from landscape.lib.amp import MethodCallSender10from landscape.lib.amp import MethodCallSender
10from landscape.reactor import TwistedReactor11from landscape.reactor import TwistedReactor, FakeReactor
11from landscape.lib.fetch import HTTPCodeError, PyCurlError12from landscape.lib.fetch import HTTPCodeError, PyCurlError
12from landscape.configuration import (13from landscape.configuration import (
13 print_text, LandscapeSetupScript, LandscapeSetupConfiguration,14 print_text, LandscapeSetupScript, LandscapeSetupConfiguration,
@@ -1634,7 +1635,7 @@
1634 registration_mock = self.mocker.replace(service.registration)1635 registration_mock = self.mocker.replace(service.registration)
1635 config_mock = self.mocker.replace(service.config)1636 config_mock = self.mocker.replace(service.config)
1636 print_text_mock = self.mocker.replace(print_text)1637 print_text_mock = self.mocker.replace(print_text)
1637 reactor_mock = self.mocker.patch(TwistedReactor)1638 reactor_mock = self.mocker.patch(FakeReactor)
16381639
1639 # This must necessarily happen in the following order.1640 # This must necessarily happen in the following order.
1640 self.mocker.order()1641 self.mocker.order()
@@ -1646,8 +1647,6 @@
1646 time_mock.sleep(ANY)1647 time_mock.sleep(ANY)
1647 self.mocker.count(1)1648 self.mocker.count(1)
16481649
1649 reactor_mock.run()
1650
1651 # After a nice dance the configuration is reloaded.1650 # After a nice dance the configuration is reloaded.
1652 config_mock.reload()1651 config_mock.reload()
16531652
@@ -1665,6 +1664,10 @@
16651664
1666 reactor_mock.stop()1665 reactor_mock.stop()
16671666
1667 # This is actually called after everything else since all deferreds
1668 # are synchronous and callbacks will be executed immediately.
1669 reactor_mock.run()
1670
1668 # Nothing else is printed!1671 # Nothing else is printed!
1669 print_text_mock(ANY)1672 print_text_mock(ANY)
1670 self.mocker.count(0)1673 self.mocker.count(0)
@@ -1672,7 +1675,8 @@
1672 self.mocker.replay()1675 self.mocker.replay()
16731676
1674 # DO IT!1677 # DO IT!
1675 return register(self.config, print_text, sys.exit)1678 return register(self.config, print_text, sys.exit,
1679 reactor=FakeReactor())
16761680
1677 def test_register_failure(self):1681 def test_register_failure(self):
1678 """1682 """
@@ -1685,7 +1689,7 @@
1685 registration_mock = self.mocker.replace(service.registration)1689 registration_mock = self.mocker.replace(service.registration)
1686 config_mock = self.mocker.replace(service.config)1690 config_mock = self.mocker.replace(service.config)
1687 print_text_mock = self.mocker.replace(print_text)1691 print_text_mock = self.mocker.replace(print_text)
1688 reactor_mock = self.mocker.patch(TwistedReactor)1692 reactor_mock = self.mocker.patch(FakeReactor)
16891693
1690 # This must necessarily happen in the following order.1694 # This must necessarily happen in the following order.
1691 self.mocker.order()1695 self.mocker.order()
@@ -1697,8 +1701,6 @@
1697 time_mock.sleep(ANY)1701 time_mock.sleep(ANY)
1698 self.mocker.count(1)1702 self.mocker.count(1)
16991703
1700 reactor_mock.run()
1701
1702 # After a nice dance the configuration is reloaded.1704 # After a nice dance the configuration is reloaded.
1703 config_mock.reload()1705 config_mock.reload()
17041706
@@ -1717,6 +1719,10 @@
17171719
1718 reactor_mock.stop()1720 reactor_mock.stop()
17191721
1722 # This is actually called after everything else since all deferreds
1723 # are synchronous and callbacks will be executed immediately.
1724 reactor_mock.run()
1725
1720 # Nothing else is printed!1726 # Nothing else is printed!
1721 print_text_mock(ANY)1727 print_text_mock(ANY)
1722 self.mocker.count(0)1728 self.mocker.count(0)
@@ -1724,7 +1730,9 @@
1724 self.mocker.replay()1730 self.mocker.replay()
17251731
1726 # DO IT!1732 # DO IT!
1727 return register(self.config, print_text, sys.exit)1733 exit = []
1734 register(self.config, print_text, exit.append, reactor=FakeReactor())
1735 self.assertEqual([2], exit)
17281736
1729 def test_register_exchange_failure(self):1737 def test_register_exchange_failure(self):
1730 """1738 """
@@ -1736,7 +1744,7 @@
1736 registration_mock = self.mocker.replace(service.registration)1744 registration_mock = self.mocker.replace(service.registration)
1737 config_mock = self.mocker.replace(service.config)1745 config_mock = self.mocker.replace(service.config)
1738 print_text_mock = self.mocker.replace(print_text)1746 print_text_mock = self.mocker.replace(print_text)
1739 reactor_mock = self.mocker.patch(TwistedReactor)1747 reactor_mock = self.mocker.patch(FakeReactor)
17401748
1741 # This must necessarily happen in the following order.1749 # This must necessarily happen in the following order.
1742 self.mocker.order()1750 self.mocker.order()
@@ -1748,8 +1756,6 @@
1748 time_mock.sleep(ANY)1756 time_mock.sleep(ANY)
1749 self.mocker.count(1)1757 self.mocker.count(1)
17501758
1751 reactor_mock.run()
1752
1753 # After a nice dance the configuration is reloaded.1759 # After a nice dance the configuration is reloaded.
1754 config_mock.reload()1760 config_mock.reload()
17551761
@@ -1767,6 +1773,10 @@
17671773
1768 reactor_mock.stop()1774 reactor_mock.stop()
17691775
1776 # This is actually called after everything else since all deferreds
1777 # are synchronous and callbacks will be executed immediately.
1778 reactor_mock.run()
1779
1770 # Nothing else is printed!1780 # Nothing else is printed!
1771 print_text_mock(ANY)1781 print_text_mock(ANY)
1772 self.mocker.count(0)1782 self.mocker.count(0)
@@ -1774,7 +1784,9 @@
1774 self.mocker.replay()1784 self.mocker.replay()
17751785
1776 # DO IT!1786 # DO IT!
1777 return register(self.config, print_text, sys.exit)1787 exit = []
1788 register(self.config, print_text, exit.append, reactor=FakeReactor())
1789 self.assertEqual([2], exit)
17781790
1779 def test_register_timeout_failure(self):1791 def test_register_timeout_failure(self):
1780 service = self.broker_service1792 service = self.broker_service
@@ -1782,7 +1794,7 @@
1782 registration_mock = self.mocker.replace(service.registration)1794 registration_mock = self.mocker.replace(service.registration)
1783 config_mock = self.mocker.replace(service.config)1795 config_mock = self.mocker.replace(service.config)
1784 print_text_mock = self.mocker.replace(print_text)1796 print_text_mock = self.mocker.replace(print_text)
1785 reactor_mock = self.mocker.patch(TwistedReactor)1797 reactor_mock = self.mocker.patch(FakeReactor)
1786 remote_mock = self.mocker.patch(RemoteBroker)1798 remote_mock = self.mocker.patch(RemoteBroker)
17871799
1788 protocol_mock = self.mocker.patch(MethodCallSender)1800 protocol_mock = self.mocker.patch(MethodCallSender)
@@ -1800,17 +1812,19 @@
1800 time_mock.sleep(ANY)1812 time_mock.sleep(ANY)
1801 self.mocker.count(1)1813 self.mocker.count(1)
18021814
1803 reactor_mock.run()1815 # After a nice dance the configuration is reloaded.
1816 config_mock.reload()
18041817
1805 remote_mock.call_on_event(ANY)1818 remote_mock.call_on_event(ANY)
1806 self.mocker.result(succeed(None))1819 self.mocker.result(succeed(None))
18071820
1808 # After a nice dance the configuration is reloaded.
1809 config_mock.reload()
1810
1811 registration_mock.register()1821 registration_mock.register()
1812 self.mocker.passthrough()1822 self.mocker.passthrough()
18131823
1824 # This is actually called after everything else since all deferreds
1825 # are synchronous and callbacks will be executed immediately.
1826 reactor_mock.run()
1827
1814 # Nothing else is printed!1828 # Nothing else is printed!
1815 print_text_mock(ANY)1829 print_text_mock(ANY)
1816 self.mocker.count(0)1830 self.mocker.count(0)
@@ -1818,7 +1832,12 @@
1818 self.mocker.replay()1832 self.mocker.replay()
18191833
1820 # DO IT!1834 # DO IT!
1821 return register(self.config, print_text, sys.exit)1835 fake_reactor = FakeReactor()
1836 fake_reactor._reactor = Clock()
1837 deferred = register(self.config, print_text, sys.exit,
1838 reactor=fake_reactor)
1839 fake_reactor._reactor.advance(100)
1840 return deferred
18221841
1823 def test_register_bus_connection_failure(self):1842 def test_register_bus_connection_failure(self):
1824 """1843 """
@@ -1993,14 +2012,13 @@
1993 remote_broker = self.mocker.mock()2012 remote_broker = self.mocker.mock()
19942013
1995 print_text_mock = self.mocker.replace(print_text)2014 print_text_mock = self.mocker.replace(print_text)
1996 reactor_mock = self.mocker.patch(TwistedReactor)2015 reactor_mock = self.mocker.patch(FakeReactor)
19972016
1998 # This is unordered. It's just way too much of a pain.2017 # This is unordered. It's just way too much of a pain.
1999 print_text_mock("Please wait... ", "")2018 print_text_mock("Please wait... ", "")
2000 time_mock = self.mocker.replace("time")2019 time_mock = self.mocker.replace("time")
2001 time_mock.sleep(ANY)2020 time_mock.sleep(ANY)
2002 self.mocker.count(1)2021 self.mocker.count(1)
2003 reactor_mock.run()
20042022
2005 # SNORE2023 # SNORE
2006 connector = connector_factory(ANY, configuration)2024 connector = connector_factory(ANY, configuration)
@@ -2025,10 +2043,12 @@
2025 # WHOAH DUDE. This waits for callLater(0, reactor.stop).2043 # WHOAH DUDE. This waits for callLater(0, reactor.stop).
2026 connector.disconnect()2044 connector.disconnect()
2027 reactor_mock.stop()2045 reactor_mock.stop()
2046 reactor_mock.run()
20282047
2029 self.mocker.replay()2048 self.mocker.replay()
20302049
2031 return register(configuration, print_text, sys.exit, max_retries=0)2050 return register(configuration, print_text, sys.exit, max_retries=0,
2051 reactor=FakeReactor())
20322052
20332053
2034class SSLCertificateDataTest(LandscapeConfigurationTest):2054class SSLCertificateDataTest(LandscapeConfigurationTest):
20352055
=== modified file 'landscape/tests/test_service.py'
--- landscape/tests/test_service.py 2013-04-22 09:32:25 +0000
+++ landscape/tests/test_service.py 2013-05-02 08:30:35 +0000
@@ -7,7 +7,6 @@
7from landscape.reactor import FakeReactor7from landscape.reactor import FakeReactor
8from landscape.deployment import Configuration8from landscape.deployment import Configuration
9from landscape.service import LandscapeService9from landscape.service import LandscapeService
10from landscape.amp import RemoteComponentConnector
11from landscape.tests.helpers import LandscapeTest10from landscape.tests.helpers import LandscapeTest
1211
1312
@@ -15,10 +14,6 @@
15 name = "monitor"14 name = "monitor"
1615
1716
18class RemoteTestComponentCreator(RemoteComponentConnector):
19 component = TestComponent
20
21
22class TestService(LandscapeService):17class TestService(LandscapeService):
23 service_name = TestComponent.name18 service_name = TestComponent.name
2419
2520
=== modified file 'landscape/tests/test_watchdog.py'
--- landscape/tests/test_watchdog.py 2013-04-29 11:44:32 +0000
+++ landscape/tests/test_watchdog.py 2013-05-02 08:30:35 +0000
@@ -22,7 +22,7 @@
22from landscape.lib.dns import discover_server22from landscape.lib.dns import discover_server
23from landscape.configuration import (23from landscape.configuration import (
24 fetch_base64_ssl_public_certificate, print_text)24 fetch_base64_ssl_public_certificate, print_text)
25from landscape.amp import ComponentProtocolClientFactory, RemoteComponentConnector25from landscape.amp import ComponentConnector
26from landscape.broker.amp import RemoteBrokerConnector26from landscape.broker.amp import RemoteBrokerConnector
27from landscape.deployment import Configuration27from landscape.deployment import Configuration
28from landscape.reactor import TwistedReactor28from landscape.reactor import TwistedReactor
@@ -472,15 +472,9 @@
472 name = "broker"472 name = "broker"
473473
474474
475class StubBrokerProtocolFactory(ComponentProtocolClientFactory):475class RemoteStubBrokerConnector(ComponentConnector):
476
477 initialDelay = 0.1
478
479
480class RemoteStubBrokerConnector(RemoteComponentConnector):
481476
482 component = StubBroker477 component = StubBroker
483 factory = StubBrokerProtocolFactory
484478
485479
486class DaemonTestBase(LandscapeTest):480class DaemonTestBase(LandscapeTest):
@@ -945,6 +939,7 @@
945 return RemoteBrokerConnector939 return RemoteBrokerConnector
946940
947 def test_is_running(self):941 def test_is_running(self):
942 self.daemon._connector._reactor = self.broker_service.reactor
948 result = self.daemon.is_running()943 result = self.daemon.is_running()
949 result.addCallback(self.assertTrue)944 result.addCallback(self.assertTrue)
950 return result945 return result
951946
=== modified file 'landscape/watchdog.py'
--- landscape/watchdog.py 2013-04-22 09:32:25 +0000
+++ landscape/watchdog.py 2013-05-02 08:30:35 +0000
@@ -68,7 +68,7 @@
68 @cvar factor: The factor by which the delay between subsequent connection68 @cvar factor: The factor by which the delay between subsequent connection
69 attempts will increase.69 attempts will increase.
7070
71 @param connector: The L{RemoteComponentConnector} of the daemon.71 @param connector: The L{ComponentConnector} of the daemon.
72 @param reactor: The reactor used to spawn the process and schedule timed72 @param reactor: The reactor used to spawn the process and schedule timed
73 calls.73 calls.
74 @param verbose: Optionally, report more information when running this74 @param verbose: Optionally, report more information when running this

Subscribers

People subscribed via source and target branches

to all changes: