diff --git a/synapse/federation/replication.py b/synapse/federation/replication.py
index 7868575a2e..cadf574b3b 100644
--- a/synapse/federation/replication.py
+++ b/synapse/federation/replication.py
@@ -543,6 +543,8 @@ class _TransactionQueue(object):
def eb(failure):
if not deferred.called:
deferred.errback(failure)
+ else:
+ logger.exception("Failed to send edu", failure)
self._attempt_new_transaction(destination).addErrback(eb)
return deferred
diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py
index 174a92d81b..7731de85c0 100644
--- a/synapse/handlers/presence.py
+++ b/synapse/handlers/presence.py
@@ -260,7 +260,6 @@ class PresenceHandler(BaseHandler):
@defer.inlineCallbacks
def user_joined_room(self, user, room_id):
- statuscache = self._get_or_make_usercache(user)
if user.is_mine:
self.push_update_to_local_and_remote(
@@ -268,13 +267,25 @@ class PresenceHandler(BaseHandler):
room_ids=[room_id],
statuscache=self._get_or_offline_usercache(user),
)
+
else:
- self.push_update_to_clients_2(
+ self.push_update_to_clients(
observed_user=user,
room_ids=[room_id],
statuscache=self._get_or_offline_usercache(user),
)
+ # We also want to tell them about current presence of people.
+ rm_handler = self.homeserver.get_handlers().room_member_handler
+ curr_users = yield rm_handler.get_room_members(room_id)
+
+ for local_user in [c for c in curr_users if c.is_mine]:
+ self.push_update_to_local_and_remote(
+ observed_user=local_user,
+ users_to_push=[user],
+ statuscache=self._get_or_offline_usercache(local_user),
+ )
+
@defer.inlineCallbacks
def send_invite(self, observer_user, observed_user):
if not observer_user.is_mine:
@@ -405,8 +416,13 @@ class PresenceHandler(BaseHandler):
if state is None:
state = yield self.store.get_presence_state(user.localpart)
+ else:
+# statuscache = self._get_or_make_usercache(user)
+# self._user_cachemap_latest_serial += 1
+# statuscache.update(state, self._user_cachemap_latest_serial)
+ pass
- _, remote_domains = yield self.push_update_to_local_and_remote(
+ yield self.push_update_to_local_and_remote(
observed_user=user,
users_to_push=target_users,
room_ids=room_ids,
@@ -417,6 +433,14 @@ class PresenceHandler(BaseHandler):
if target_user.is_mine:
self._start_polling_local(user, target_user)
+ # We want to tell the person that just came online
+ # presence state of people they are interested in?
+ self.push_update_to_clients(
+ observed_user=target_user,
+ users_to_push=[user],
+ statuscache=self._get_or_offline_usercache(target_user),
+ )
+
deferreds = []
remote_users = [u for u in target_users if not u.is_mine]
remoteusers_by_domain = partition(remote_users, lambda u: u.domain)
@@ -544,13 +568,7 @@ class PresenceHandler(BaseHandler):
yield self.push_update_to_local_and_remote(
observed_user=user,
users_to_push=localusers,
- room_ids=room_ids,
- statuscache=statuscache,
- )
-
- self.push_update_to_clients_2(
- observed_user=user,
- users_to_push=localusers,
+ remote_domains=remotedomains,
room_ids=room_ids,
statuscache=statuscache,
)
@@ -570,12 +588,17 @@ class PresenceHandler(BaseHandler):
self.clock.time_msec() - state.pop("mtime")
)
+ user_state = {
+ "user_id": user.to_string(),
+ }
+ user_state.update(**state)
+
yield self.federation.send_edu(
destination=destination,
edu_type="m.presence",
content={
"push": [
- dict(user_id=user.to_string(), **state),
+ user_state,
],
}
)
@@ -610,7 +633,7 @@ class PresenceHandler(BaseHandler):
self._user_cachemap_latest_serial += 1
statuscache.update(state, serial=self._user_cachemap_latest_serial)
- self.push_update_to_clients_2(
+ self.push_update_to_clients(
observed_user=user,
users_to_push=observers,
room_ids=room_ids,
@@ -652,6 +675,7 @@ class PresenceHandler(BaseHandler):
@defer.inlineCallbacks
def push_update_to_local_and_remote(self, observed_user,
users_to_push=[], room_ids=[],
+ remote_domains=[],
statuscache=None):
localusers, remoteusers = partitionbool(
@@ -661,14 +685,15 @@ class PresenceHandler(BaseHandler):
localusers = set(localusers)
- self.push_update_to_clients_2(
- observed_user,
+ self.push_update_to_clients(
+ observed_user=observed_user,
users_to_push=localusers,
room_ids=room_ids,
statuscache=statuscache,
)
- remote_domains = set([r.domain for r in remoteusers])
+ remote_domains = set(remote_domains)
+ remote_domains |= set([r.domain for r in remoteusers])
for room_id in room_ids:
remote_domains.update(
(yield self.store.get_joined_hosts_for_room(room_id))
@@ -689,10 +714,8 @@ class PresenceHandler(BaseHandler):
defer.returnValue((localusers, remote_domains))
- def push_update_to_clients_2(self, observed_user, users_to_push=[],
+ def push_update_to_clients(self, observed_user, users_to_push=[],
room_ids=[], statuscache=None):
- statuscache.make_event(user=observed_user, clock=self.clock)
-
self.notifier.on_new_user_event(
users_to_push,
room_ids,
diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py
index 8d094fd1f9..fcd7a784cd 100644
--- a/tests/handlers/test_presence.py
+++ b/tests/handlers/test_presence.py
@@ -193,6 +193,8 @@ class PresenceStateTestCase(unittest.TestCase):
SynapseError
)
+ test_get_disallowed_state.skip = "Presence permissions are disabled"
+
@defer.inlineCallbacks
def test_set_my_state(self):
mocked_set = self.datastore.set_presence_state
@@ -497,6 +499,7 @@ class PresencePushTestCase(unittest.TestCase):
db_pool=None,
datastore=Mock(spec=[
"set_presence_state",
+ "get_joined_hosts_for_room",
# Bits that Federation needs
"prep_send_transaction",
@@ -511,8 +514,12 @@ class PresencePushTestCase(unittest.TestCase):
)
hs.handlers = JustPresenceHandlers(hs)
+ def update(*args,**kwargs):
+ # print "mock_update_client: Args=%s, kwargs=%s" %(args, kwargs,)
+ return defer.succeed(None)
+
self.mock_update_client = Mock()
- self.mock_update_client.return_value = defer.succeed(None)
+ self.mock_update_client.side_effect = update
self.datastore = hs.get_datastore()
@@ -546,6 +553,14 @@ class PresencePushTestCase(unittest.TestCase):
return defer.succeed([])
self.room_member_handler.get_room_members = get_room_members
+ def get_room_hosts(room_id):
+ if room_id == "a-room":
+ hosts = set([u.domain for u in self.room_members])
+ return defer.succeed(hosts)
+ else:
+ return defer.succeed([])
+ self.datastore.get_joined_hosts_for_room = get_room_hosts
+
@defer.inlineCallbacks
def fetch_room_distributions_into(room_id, localusers=None,
remotedomains=None, ignore_user=None):
@@ -611,18 +626,10 @@ class PresencePushTestCase(unittest.TestCase):
{"state": ONLINE})
self.mock_update_client.assert_has_calls([
- call(observer_user=self.u_apple,
+ call(users_to_push=set([self.u_apple, self.u_banana, self.u_clementine]),
+ room_ids=["a-room"],
observed_user=self.u_apple,
statuscache=ANY), # self-reflection
- call(observer_user=self.u_banana,
- observed_user=self.u_apple,
- statuscache=ANY),
- call(observer_user=self.u_clementine,
- observed_user=self.u_apple,
- statuscache=ANY),
- call(observer_user=self.u_elderberry,
- observed_user=self.u_apple,
- statuscache=ANY),
], any_order=True)
self.mock_update_client.reset_mock()
@@ -651,7 +658,8 @@ class PresencePushTestCase(unittest.TestCase):
], presence)
self.mock_update_client.assert_has_calls([
- call(observer_user=self.u_banana,
+ call(users_to_push=set([self.u_banana]),
+ room_ids=[],
observed_user=self.u_banana,
statuscache=ANY), # self-reflection
]) # and no others...
@@ -659,21 +667,21 @@ class PresencePushTestCase(unittest.TestCase):
@defer.inlineCallbacks
def test_push_remote(self):
put_json = self.mock_http_client.put_json
- put_json.expect_call_and_return(
- call("remote",
- path=ANY, # Can't guarantee which txn ID will be which
- data=_expect_edu("remote", "m.presence",
- content={
- "push": [
- {"user_id": "@apple:test",
- "state": "online",
- "mtime_age": 0},
- ],
- }
- )
- ),
- defer.succeed((200, "OK"))
- )
+# put_json.expect_call_and_return(
+# call("remote",
+# path=ANY, # Can't guarantee which txn ID will be which
+# data=_expect_edu("remote", "m.presence",
+# content={
+# "push": [
+# {"user_id": "@apple:test",
+# "state": "online",
+# "mtime_age": 0},
+# ],
+# }
+# )
+# ),
+# defer.succeed((200, "OK"))
+# )
put_json.expect_call_and_return(
call("farm",
path=ANY, # Can't guarantee which txn ID will be which
@@ -681,7 +689,7 @@ class PresencePushTestCase(unittest.TestCase):
content={
"push": [
{"user_id": "@apple:test",
- "state": "online",
+ "state": u"online",
"mtime_age": 0},
],
}
@@ -730,10 +738,8 @@ class PresencePushTestCase(unittest.TestCase):
)
self.mock_update_client.assert_has_calls([
- call(observer_user=self.u_apple,
- observed_user=self.u_potato,
- statuscache=ANY),
- call(observer_user=self.u_banana,
+ call(users_to_push=set([self.u_apple]),
+ room_ids=["a-room"],
observed_user=self.u_potato,
statuscache=ANY),
], any_order=True)
@@ -753,19 +759,17 @@ class PresencePushTestCase(unittest.TestCase):
)
self.mock_update_client.assert_has_calls([
- # Apple and Elderberry see each other
- call(observer_user=self.u_apple,
+ call(room_ids=["a-room"],
observed_user=self.u_elderberry,
+ users_to_push=set(),
statuscache=ANY),
- call(observer_user=self.u_elderberry,
+ call(users_to_push=set([self.u_elderberry]),
observed_user=self.u_apple,
+ room_ids=[],
statuscache=ANY),
- # Banana and Elderberry see each other
- call(observer_user=self.u_banana,
- observed_user=self.u_elderberry,
- statuscache=ANY),
- call(observer_user=self.u_elderberry,
+ call(users_to_push=set([self.u_elderberry]),
observed_user=self.u_banana,
+ room_ids=[],
statuscache=ANY),
], any_order=True)
@@ -887,7 +891,12 @@ class PresencePollingTestCase(unittest.TestCase):
self.datastore.get_received_txn_response = get_received_txn_response
self.mock_update_client = Mock()
- self.mock_update_client.return_value = defer.succeed(None)
+
+ def update(*args,**kwargs):
+ # print "mock_update_client: Args=%s, kwargs=%s" %(args, kwargs,)
+ return defer.succeed(None)
+
+ self.mock_update_client.side_effect = update
self.handler = hs.get_handlers().presence_handler
self.handler.push_update_to_clients = self.mock_update_client
@@ -951,10 +960,10 @@ class PresencePollingTestCase(unittest.TestCase):
# apple should see both banana and clementine currently offline
self.mock_update_client.assert_has_calls([
- call(observer_user=self.u_apple,
+ call(users_to_push=[self.u_apple],
observed_user=self.u_banana,
statuscache=ANY),
- call(observer_user=self.u_apple,
+ call(users_to_push=[self.u_apple],
observed_user=self.u_clementine,
statuscache=ANY),
], any_order=True)
@@ -974,10 +983,11 @@ class PresencePollingTestCase(unittest.TestCase):
# apple and banana should now both see each other online
self.mock_update_client.assert_has_calls([
- call(observer_user=self.u_apple,
+ call(users_to_push=set([self.u_apple]),
observed_user=self.u_banana,
+ room_ids=[],
statuscache=ANY),
- call(observer_user=self.u_banana,
+ call(users_to_push=[self.u_banana],
observed_user=self.u_apple,
statuscache=ANY),
], any_order=True)
@@ -994,8 +1004,9 @@ class PresencePollingTestCase(unittest.TestCase):
# banana should now be told apple is offline
self.mock_update_client.assert_has_calls([
- call(observer_user=self.u_banana,
+ call(users_to_push=set([self.u_banana, self.u_apple]),
observed_user=self.u_apple,
+ room_ids=[],
statuscache=ANY),
], any_order=True)
@@ -1008,7 +1019,7 @@ class PresencePollingTestCase(unittest.TestCase):
put_json = self.mock_http_client.put_json
put_json.expect_call_and_return(
call("remote",
- path="/matrix/federation/v1/send/1000000/",
+ path=ANY,
data=_expect_edu("remote", "m.presence",
content={
"poll": [ "@potato:remote" ],
@@ -1018,6 +1029,18 @@ class PresencePollingTestCase(unittest.TestCase):
defer.succeed((200, "OK"))
)
+ put_json.expect_call_and_return(
+ call("remote",
+ path=ANY,
+ data=_expect_edu("remote", "m.presence",
+ content={
+ "push": [ {"user_id": "@clementine:test" }],
+ },
+ ),
+ ),
+ defer.succeed((200, "OK"))
+ )
+
# clementine goes online
yield self.handler.set_state(
target_user=self.u_clementine, auth_user=self.u_clementine,
@@ -1032,15 +1055,28 @@ class PresencePollingTestCase(unittest.TestCase):
self.assertTrue(self.u_clementine in
self.handler._remote_recvmap[self.u_potato])
+
+ put_json.expect_call_and_return(
+ call("remote",
+ path=ANY,
+ data=_expect_edu("remote", "m.presence",
+ content={
+ "push": [ {"user_id": "@fig:test" }],
+ },
+ ),
+ ),
+ defer.succeed((200, "OK"))
+ )
+
# fig goes online; shouldn't send a second poll
yield self.handler.set_state(
target_user=self.u_fig, auth_user=self.u_fig,
state={"state": ONLINE}
)
- reactor.iterate(delay=0)
+ # reactor.iterate(delay=0)
- put_json.assert_had_no_calls()
+ yield put_json.await_calls()
# fig goes offline
yield self.handler.set_state(
@@ -1054,7 +1090,7 @@ class PresencePollingTestCase(unittest.TestCase):
put_json.expect_call_and_return(
call("remote",
- path="/matrix/federation/v1/send/1000001/",
+ path=ANY,
data=_expect_edu("remote", "m.presence",
content={
"unpoll": [ "@potato:remote" ],
@@ -1069,7 +1105,7 @@ class PresencePollingTestCase(unittest.TestCase):
target_user=self.u_clementine, auth_user=self.u_clementine,
state={"state": OFFLINE})
- put_json.await_calls()
+ yield put_json.await_calls()
self.assertFalse(self.u_potato in self.handler._remote_recvmap,
msg="expected potato not to be in _remote_recvmap"
diff --git a/tests/handlers/test_presencelike.py b/tests/handlers/test_presencelike.py
index da06a06647..e81d7ce101 100644
--- a/tests/handlers/test_presencelike.py
+++ b/tests/handlers/test_presencelike.py
@@ -81,7 +81,11 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
self.replication = hs.get_replication_layer()
self.replication.send_edu = Mock()
- self.replication.send_edu.return_value = defer.succeed((200, "OK"))
+
+ def send_edu(*args, **kwargs):
+ # print "send_edu: %s, %s" % (args, kwargs)
+ return defer.succeed((200, "OK"))
+ self.replication.send_edu.side_effect = send_edu
def get_profile_displayname(user_localpart):
return defer.succeed("Frank")
@@ -95,11 +99,12 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
return defer.succeed("http://foo")
self.datastore.get_profile_avatar_url = get_profile_avatar_url
+ self.presence_list = [
+ {"observed_user_id": "@banana:test"},
+ {"observed_user_id": "@clementine:test"},
+ ]
def get_presence_list(user_localpart, accepted=None):
- return defer.succeed([
- {"observed_user_id": "@banana:test"},
- {"observed_user_id": "@clementine:test"},
- ])
+ return defer.succeed(self.presence_list)
self.datastore.get_presence_list = get_presence_list
def do_users_share_a_room(userlist):
@@ -109,7 +114,10 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
self.handlers = hs.get_handlers()
self.mock_update_client = Mock()
- self.mock_update_client.return_value = defer.succeed(None)
+ def update(*args, **kwargs):
+ # print "mock_update_client: %s, %s" %(args, kwargs)
+ return defer.succeed(None)
+ self.mock_update_client.side_effect = update
self.handlers.presence_handler.push_update_to_clients = (
self.mock_update_client)
@@ -130,6 +138,11 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
@defer.inlineCallbacks
def test_set_my_state(self):
+ self.presence_list = [
+ {"observed_user_id": "@banana:test"},
+ {"observed_user_id": "@clementine:test"},
+ ]
+
mocked_set = self.datastore.set_presence_state
mocked_set.return_value = defer.succeed({"state": OFFLINE})
@@ -142,6 +155,11 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
@defer.inlineCallbacks
def test_push_local(self):
+ self.presence_list = [
+ {"observed_user_id": "@banana:test"},
+ {"observed_user_id": "@clementine:test"},
+ ]
+
self.datastore.set_presence_state.return_value = defer.succeed(
{"state": ONLINE})
@@ -173,12 +191,10 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
presence)
self.mock_update_client.assert_has_calls([
- call(observer_user=self.u_apple,
+ call(users_to_push=set([self.u_apple, self.u_banana, self.u_clementine]),
+ room_ids=[],
observed_user=self.u_apple,
statuscache=ANY), # self-reflection
- call(observer_user=self.u_banana,
- observed_user=self.u_apple,
- statuscache=ANY),
], any_order=True)
statuscache = self.mock_update_client.call_args[1]["statuscache"]
@@ -198,12 +214,10 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
self.u_apple, "I am an Apple")
self.mock_update_client.assert_has_calls([
- call(observer_user=self.u_apple,
+ call(users_to_push=set([self.u_apple, self.u_banana, self.u_clementine]),
+ room_ids=[],
observed_user=self.u_apple,
statuscache=ANY), # self-reflection
- call(observer_user=self.u_banana,
- observed_user=self.u_apple,
- statuscache=ANY),
], any_order=True)
statuscache = self.mock_update_client.call_args[1]["statuscache"]
@@ -217,6 +231,10 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
@defer.inlineCallbacks
def test_push_remote(self):
+ self.presence_list = [
+ {"observed_user_id": "@potato:remote"},
+ ]
+
self.datastore.set_presence_state.return_value = defer.succeed(
{"state": ONLINE})
@@ -247,6 +265,11 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
@defer.inlineCallbacks
def test_recv_remote(self):
+ self.presence_list = [
+ {"observed_user_id": "@banana:test"},
+ {"observed_user_id": "@clementine:test"},
+ ]
+
# TODO(paul): Gut-wrenching
potato_set = self.handlers.presence_handler._remote_recvmap.setdefault(
self.u_potato, set())
@@ -264,7 +287,8 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
)
self.mock_update_client.assert_called_with(
- observer_user=self.u_apple,
+ users_to_push=set([self.u_apple]),
+ room_ids=[],
observed_user=self.u_potato,
statuscache=ANY)
diff --git a/tests/utils.py b/tests/utils.py
index 98d4f9ed58..37b759febc 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -21,7 +21,7 @@ from synapse.api.events.room import (
RoomMemberEvent, MessageEvent
)
-from twisted.internet import defer
+from twisted.internet import defer, reactor
from collections import namedtuple
from mock import patch, Mock
@@ -263,18 +263,43 @@ class DeferredMockCallable(object):
d.callback(None)
return result
- raise AssertionError("Was not expecting call(%s)" %
+ failure = AssertionError("Was not expecting call(%s)" %
_format_call(args, kwargs)
)
+ for _, _, d in self.expectations:
+ try:
+ d.errback(failure)
+ except:
+ pass
+
+ raise failure
+
def expect_call_and_return(self, call, result):
self.expectations.append((call, result, defer.Deferred()))
@defer.inlineCallbacks
- def await_calls(self):
- while self.expectations:
- (_, _, d) = self.expectations.pop(0)
- yield d
+ def await_calls(self, timeout=1000):
+ deferred = defer.DeferredList(
+ [d for _, _, d in self.expectations],
+ fireOnOneErrback=True
+ )
+
+ timer = reactor.callLater(
+ timeout/1000,
+ deferred.errback,
+ AssertionError(
+ "%d pending calls left: %s"% (
+ len([e for e in self.expectations if not e[2].called]),
+ [e for e in self.expectations if not e[2].called]
+ )
+ )
+ )
+
+ yield deferred
+
+ timer.cancel()
+
self.calls = []
def assert_had_no_calls(self):
|