summary refs log tree commit diff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/__init__.py1
-rw-r--r--tests/api/test_auth.py4
-rw-r--r--tests/api/test_filtering.py30
-rw-r--r--tests/api/test_ratelimiting.py1
-rw-r--r--tests/appservice/test_appservice.py2
-rw-r--r--tests/appservice/test_scheduler.py4
-rw-r--r--tests/config/test_load.py21
-rw-r--r--tests/crypto/__init__.py1
-rw-r--r--tests/events/test_utils.py1
-rw-r--r--tests/handlers/test_appservice.py2
-rw-r--r--tests/handlers/test_auth.py1
-rw-r--r--tests/handlers/test_presence.py1482
-rw-r--r--tests/handlers/test_presencelike.py311
-rw-r--r--tests/handlers/test_profile.py12
-rw-r--r--tests/handlers/test_typing.py115
-rw-r--r--tests/metrics/test_metric.py3
-rw-r--r--tests/replication/__init__.py14
-rw-r--r--tests/replication/test_resource.py179
-rw-r--r--tests/rest/client/v1/__init__.py1
-rw-r--r--tests/rest/client/v1/test_presence.py412
-rw-r--r--tests/rest/client/v1/test_profile.py56
-rw-r--r--tests/rest/client/v1/test_rooms.py226
-rw-r--r--tests/rest/client/v1/test_typing.py44
-rw-r--r--tests/rest/client/v1/utils.py5
-rw-r--r--tests/rest/client/v2_alpha/__init__.py2
-rw-r--r--tests/rest/client/v2_alpha/test_filter.py19
-rw-r--r--tests/rest/client/v2_alpha/test_register.py4
-rw-r--r--tests/storage/event_injector.py8
-rw-r--r--tests/storage/test__base.py8
-rw-r--r--tests/storage/test_appservice.py12
-rw-r--r--tests/storage/test_background_update.py8
-rw-r--r--tests/storage/test_base.py89
-rw-r--r--tests/storage/test_events.py1
-rw-r--r--tests/storage/test_presence.py26
-rw-r--r--tests/storage/test_profile.py2
-rw-r--r--tests/storage/test_registration.py6
-rw-r--r--tests/storage/test_room.py14
-rw-r--r--tests/storage/test_roommember.py22
-rw-r--r--tests/storage/test_stream.py4
-rw-r--r--tests/test_distributor.py11
-rw-r--r--tests/test_test_utils.py3
-rw-r--r--tests/unittest.py7
-rw-r--r--tests/util/__init__.py1
-rw-r--r--tests/util/test_dict_cache.py1
-rw-r--r--tests/util/test_snapshot_cache.py1
-rw-r--r--tests/util/test_treecache.py7
-rw-r--r--tests/util/test_wheel_timer.py74
-rw-r--r--tests/utils.py39
48 files changed, 948 insertions, 2349 deletions
diff --git a/tests/__init__.py b/tests/__init__.py
index d0e9399dda..bfebb0f644 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -12,4 +12,3 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
diff --git a/tests/api/test_auth.py b/tests/api/test_auth.py
index 474c5c418f..7e7b0b4b1d 100644
--- a/tests/api/test_auth.py
+++ b/tests/api/test_auth.py
@@ -281,9 +281,9 @@ class AuthTestCase(unittest.TestCase):
         macaroon.add_first_party_caveat("gen = 1")
         macaroon.add_first_party_caveat("type = access")
         macaroon.add_first_party_caveat("user_id = %s" % (user,))
-        macaroon.add_first_party_caveat("time < 1") # ms
+        macaroon.add_first_party_caveat("time < 1")  # ms
 
-        self.hs.clock.now = 5000 # seconds
+        self.hs.clock.now = 5000  # seconds
 
         yield self.auth.get_user_from_macaroon(macaroon.serialize())
         # TODO(daniel): Turn on the check that we validate expiration, when we
diff --git a/tests/api/test_filtering.py b/tests/api/test_filtering.py
index ceb0089268..dcb6c5bc31 100644
--- a/tests/api/test_filtering.py
+++ b/tests/api/test_filtering.py
@@ -21,7 +21,6 @@ from tests.utils import (
     MockHttpResource, DeferredMockCallable, setup_test_homeserver
 )
 
-from synapse.types import UserID
 from synapse.api.filtering import Filter
 from synapse.events import FrozenEvent
 
@@ -356,7 +355,6 @@ class FilteringTestCase(unittest.TestCase):
                 "types": ["m.*"]
             }
         }
-        user = UserID.from_string("@" + user_localpart + ":test")
         filter_id = yield self.datastore.add_user_filter(
             user_localpart=user_localpart,
             user_filter=user_filter_json,
@@ -411,7 +409,6 @@ class FilteringTestCase(unittest.TestCase):
                 }
             }
         }
-        user = UserID.from_string("@" + user_localpart + ":test")
         filter_id = yield self.datastore.add_user_filter(
             user_localpart=user_localpart,
             user_filter=user_filter_json,
@@ -440,7 +437,6 @@ class FilteringTestCase(unittest.TestCase):
                 }
             }
         }
-        user = UserID.from_string("@" + user_localpart + ":test")
         filter_id = yield self.datastore.add_user_filter(
             user_localpart=user_localpart,
             user_filter=user_filter_json,
@@ -460,6 +456,22 @@ class FilteringTestCase(unittest.TestCase):
         results = user_filter.filter_room_state(events)
         self.assertEquals([], results)
 
+    def test_filter_rooms(self):
+        definition = {
+            "rooms": ["!allowed:example.com", "!excluded:example.com"],
+            "not_rooms": ["!excluded:example.com"],
+        }
+
+        room_ids = [
+            "!allowed:example.com",  # Allowed because in rooms and not in not_rooms.
+            "!excluded:example.com",  # Disallowed because in not_rooms.
+            "!not_included:example.com",  # Disallowed because not in rooms.
+        ]
+
+        filtered_room_ids = list(Filter(definition).filter_rooms(room_ids))
+
+        self.assertEquals(filtered_room_ids, ["!allowed:example.com"])
+
     @defer.inlineCallbacks
     def test_add_filter(self):
         user_filter_json = {
@@ -476,12 +488,12 @@ class FilteringTestCase(unittest.TestCase):
         )
 
         self.assertEquals(filter_id, 0)
-        self.assertEquals(user_filter_json,
-            (yield self.datastore.get_user_filter(
+        self.assertEquals(user_filter_json, (
+            yield self.datastore.get_user_filter(
                 user_localpart=user_localpart,
                 filter_id=0,
-            ))
-        )
+            )
+        ))
 
     @defer.inlineCallbacks
     def test_get_filter(self):
@@ -504,3 +516,5 @@ class FilteringTestCase(unittest.TestCase):
         )
 
         self.assertEquals(filter.get_filter_json(), user_filter_json)
+
+        self.assertRegexpMatches(repr(filter), r"<FilterCollection \{.*\}>")
diff --git a/tests/api/test_ratelimiting.py b/tests/api/test_ratelimiting.py
index dd0bc19ecf..c45b59b36c 100644
--- a/tests/api/test_ratelimiting.py
+++ b/tests/api/test_ratelimiting.py
@@ -2,6 +2,7 @@ from synapse.api.ratelimiting import Ratelimiter
 
 from tests import unittest
 
+
 class TestRatelimiter(unittest.TestCase):
 
     def test_allowed(self):
diff --git a/tests/appservice/test_appservice.py b/tests/appservice/test_appservice.py
index ef48bbc296..d6cc1881e9 100644
--- a/tests/appservice/test_appservice.py
+++ b/tests/appservice/test_appservice.py
@@ -14,7 +14,7 @@
 # limitations under the License.
 from synapse.appservice import ApplicationService
 
-from mock import Mock, PropertyMock
+from mock import Mock
 from tests import unittest
 
 
diff --git a/tests/appservice/test_scheduler.py b/tests/appservice/test_scheduler.py
index c9c2d36210..631a229332 100644
--- a/tests/appservice/test_scheduler.py
+++ b/tests/appservice/test_scheduler.py
@@ -12,7 +12,7 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-from synapse.appservice import ApplicationServiceState, AppServiceTransaction
+from synapse.appservice import ApplicationServiceState
 from synapse.appservice.scheduler import (
     _ServiceQueuer, _TransactionController, _Recoverer
 )
@@ -235,7 +235,7 @@ class ApplicationServiceSchedulerQueuerTestCase(unittest.TestCase):
         srv_2_event2 = Mock(event_id="srv2b")
 
         send_return_list = [srv_1_defer, srv_2_defer]
-        self.txn_ctrl.send = Mock(side_effect=lambda x,y: send_return_list.pop(0))
+        self.txn_ctrl.send = Mock(side_effect=lambda x, y: send_return_list.pop(0))
 
         # send events for different ASes and make sure they are sent
         self.queuer.enqueue(srv1, srv_1_event)
diff --git a/tests/config/test_load.py b/tests/config/test_load.py
index fbbbf93fef..bf46233c5c 100644
--- a/tests/config/test_load.py
+++ b/tests/config/test_load.py
@@ -60,6 +60,22 @@ class ConfigLoadingTestCase(unittest.TestCase):
         config2 = HomeServerConfig.load_config("", ["-c", self.file])
         self.assertEqual(config1.macaroon_secret_key, config2.macaroon_secret_key)
 
+    def test_disable_registration(self):
+        self.generate_config()
+        self.add_lines_to_config([
+            "enable_registration: true",
+            "disable_registration: true",
+        ])
+        # Check that disable_registration clobbers enable_registration.
+        config = HomeServerConfig.load_config("", ["-c", self.file])
+        self.assertFalse(config.enable_registration)
+
+        # Check that either config value is clobbered by the command line.
+        config = HomeServerConfig.load_config("", [
+            "-c", self.file, "--enable-registration"
+        ])
+        self.assertTrue(config.enable_registration)
+
     def generate_config(self):
         HomeServerConfig.load_config("", [
             "--generate-config",
@@ -76,3 +92,8 @@ class ConfigLoadingTestCase(unittest.TestCase):
         contents = [l for l in contents if needle not in l]
         with open(self.file, "w") as f:
             f.write("".join(contents))
+
+    def add_lines_to_config(self, lines):
+        with open(self.file, "a") as f:
+            for line in lines:
+                f.write(line + "\n")
diff --git a/tests/crypto/__init__.py b/tests/crypto/__init__.py
index d0e9399dda..bfebb0f644 100644
--- a/tests/crypto/__init__.py
+++ b/tests/crypto/__init__.py
@@ -12,4 +12,3 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
diff --git a/tests/events/test_utils.py b/tests/events/test_utils.py
index 894d0c3845..fb0953c4ec 100644
--- a/tests/events/test_utils.py
+++ b/tests/events/test_utils.py
@@ -19,6 +19,7 @@ from .. import unittest
 from synapse.events import FrozenEvent
 from synapse.events.utils import prune_event
 
+
 class PruneEventTestCase(unittest.TestCase):
     """ Asserts that a new event constructed with `evdict` will look like
     `matchdict` when it is redacted. """
diff --git a/tests/handlers/test_appservice.py b/tests/handlers/test_appservice.py
index ba6e2c640e..7ddbbb9b4a 100644
--- a/tests/handlers/test_appservice.py
+++ b/tests/handlers/test_appservice.py
@@ -129,8 +129,6 @@ class AppServiceHandlerTestCase(unittest.TestCase):
         self.assertEquals(result.room_id, room_id)
         self.assertEquals(result.servers, servers)
 
-
-
     def _mkservice(self, is_interested):
         service = Mock()
         service.is_interested = Mock(return_value=is_interested)
diff --git a/tests/handlers/test_auth.py b/tests/handlers/test_auth.py
index 2f21bf91e5..21077cbe9a 100644
--- a/tests/handlers/test_auth.py
+++ b/tests/handlers/test_auth.py
@@ -15,7 +15,6 @@
 
 import pymacaroons
 
-from mock import Mock, NonCallableMock
 from synapse.handlers.auth import AuthHandler
 from tests import unittest
 from tests.utils import setup_test_homeserver
diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py
index 447a22b5fc..87c795fcfa 100644
--- a/tests/handlers/test_presence.py
+++ b/tests/handlers/test_presence.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2014-2016 OpenMarket Ltd
+# Copyright 2016 OpenMarket Ltd
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -15,1326 +15,370 @@
 
 
 from tests import unittest
-from twisted.internet import defer, reactor
 
-from mock import Mock, call, ANY, NonCallableMock
-import json
-
-from tests.utils import (
-    MockHttpResource, MockClock, DeferredMockCallable, setup_test_homeserver
-)
+from mock import Mock, call
 
 from synapse.api.constants import PresenceState
-from synapse.api.errors import SynapseError
-from synapse.handlers.presence import PresenceHandler, UserPresenceCache
-from synapse.streams.config import SourcePaginationConfig
-from synapse.types import UserID
-
-OFFLINE = PresenceState.OFFLINE
-UNAVAILABLE = PresenceState.UNAVAILABLE
-ONLINE = PresenceState.ONLINE
-
-
-def _expect_edu(destination, edu_type, content, origin="test"):
-    return {
-        "origin": origin,
-        "origin_server_ts": 1000000,
-        "pdus": [],
-        "edus": [
-            {
-                "edu_type": edu_type,
-                "content": content,
-            }
-        ],
-        "pdu_failures": [],
-    }
-
-def _make_edu_json(origin, edu_type, content):
-    return json.dumps(_expect_edu("test", edu_type, content, origin=origin))
-
-
-class JustPresenceHandlers(object):
-    def __init__(self, hs):
-        self.presence_handler = PresenceHandler(hs)
-
-
-class PresenceTestCase(unittest.TestCase):
-    @defer.inlineCallbacks
-    def setUp(self):
-        self.clock = MockClock()
-
-        self.mock_federation_resource = MockHttpResource()
-
-        self.mock_http_client = Mock(spec=[])
-        self.mock_http_client.put_json = DeferredMockCallable()
-
-        hs_kwargs = {}
-        if hasattr(self, "make_datastore_mock"):
-            hs_kwargs["datastore"] = self.make_datastore_mock()
-
-        hs = yield setup_test_homeserver(
-            clock=self.clock,
-            handlers=None,
-            resource_for_federation=self.mock_federation_resource,
-            http_client=self.mock_http_client,
-            keyring=Mock(),
-            **hs_kwargs
-        )
-        hs.handlers = JustPresenceHandlers(hs)
-
-        self.datastore = hs.get_datastore()
-
-        self.setUp_roommemberhandler_mocks(hs.handlers)
-
-        self.handler = hs.get_handlers().presence_handler
-        self.event_source = hs.get_event_sources().sources["presence"]
-
-        self.distributor = hs.get_distributor()
-        self.distributor.declare("user_joined_room")
-
-        yield self.setUp_users(hs)
-
-    def setUp_roommemberhandler_mocks(self, handlers):
-        self.room_id = "a-room"
-        self.room_members = []
-
-        room_member_handler = handlers.room_member_handler = Mock(spec=[
-            "get_joined_rooms_for_user",
-            "get_room_members",
-            "fetch_room_distributions_into",
-        ])
-        self.room_member_handler = room_member_handler
-
-        def get_rooms_for_user(user):
-            if user in self.room_members:
-                return defer.succeed([self.room_id])
-            else:
-                return defer.succeed([])
-        room_member_handler.get_joined_rooms_for_user = get_rooms_for_user
-
-        def get_room_members(room_id):
-            if room_id == self.room_id:
-                return defer.succeed(self.room_members)
-            else:
-                return defer.succeed([])
-        room_member_handler.get_room_members = get_room_members
-
-        @defer.inlineCallbacks
-        def fetch_room_distributions_into(room_id, localusers=None,
-                remotedomains=None, ignore_user=None):
-
-            members = yield get_room_members(room_id)
-            for member in members:
-                if ignore_user is not None and member == ignore_user:
-                    continue
-
-                if member.is_mine:
-                    if localusers is not None:
-                        localusers.add(member)
-                else:
-                    if remotedomains is not None:
-                        remotedomains.add(member.domain)
-        room_member_handler.fetch_room_distributions_into = (
-                fetch_room_distributions_into)
-
-        self.setUp_datastore_room_mocks(self.datastore)
-
-    def setUp_datastore_room_mocks(self, datastore):
-        def get_room_hosts(room_id):
-            if room_id == self.room_id:
-                hosts = set([u.domain for u in self.room_members])
-                return defer.succeed(hosts)
-            else:
-                return defer.succeed([])
-        datastore.get_joined_hosts_for_room = get_room_hosts
-
-        def user_rooms_intersect(userlist):
-            room_member_ids = map(lambda u: u.to_string(), self.room_members)
-
-            shared = all(map(lambda i: i in room_member_ids, userlist))
-            return defer.succeed(shared)
-        datastore.user_rooms_intersect = user_rooms_intersect
-
-    @defer.inlineCallbacks
-    def setUp_users(self, hs):
-        # Some local users to test with
-        self.u_apple = UserID.from_string("@apple:test")
-        self.u_banana = UserID.from_string("@banana:test")
-        self.u_clementine = UserID.from_string("@clementine:test")
-
-        for u in self.u_apple, self.u_banana, self.u_clementine:
-            yield self.datastore.create_presence(u.localpart)
-
-        yield self.datastore.set_presence_state(
-            self.u_apple.localpart, {"state": ONLINE, "status_msg": "Online"}
-        )
-
-        # ID of a local user that does not exist
-        self.u_durian = UserID.from_string("@durian:test")
-
-        # A remote user
-        self.u_cabbage = UserID.from_string("@cabbage:elsewhere")
-
-
-class MockedDatastorePresenceTestCase(PresenceTestCase):
-    def make_datastore_mock(self):
-        datastore = Mock(spec=[
-            # Bits that Federation needs
-            "prep_send_transaction",
-            "delivered_txn",
-            "get_received_txn_response",
-            "set_received_txn_response",
-            "get_destination_retry_timings",
-        ])
-
-        self.setUp_datastore_federation_mocks(datastore)
-        self.setUp_datastore_presence_mocks(datastore)
-
-        return datastore
-
-    def setUp_datastore_federation_mocks(self, datastore):
-        retry_timings_res = {
-            "destination": "",
-            "retry_last_ts": 0,
-            "retry_interval": 0,
-        }
-        datastore.get_destination_retry_timings.return_value = (
-            defer.succeed(retry_timings_res)
-        )
-
-        def get_received_txn_response(*args):
-            return defer.succeed(None)
-        datastore.get_received_txn_response = get_received_txn_response
-
-    def setUp_datastore_presence_mocks(self, datastore):
-        self.current_user_state = {
-            "apple": OFFLINE,
-            "banana": OFFLINE,
-            "clementine": OFFLINE,
-            "fig": OFFLINE,
-        }
-
-        def get_presence_state(user_localpart):
-            return defer.succeed(
-                    {"state": self.current_user_state[user_localpart],
-                     "status_msg": None,
-                     "mtime": 123456000}
-            )
-        datastore.get_presence_state = get_presence_state
-
-        def set_presence_state(user_localpart, new_state):
-            was = self.current_user_state[user_localpart]
-            self.current_user_state[user_localpart] = new_state["state"]
-            return defer.succeed({"state": was})
-        datastore.set_presence_state = set_presence_state
-
-        def get_presence_list(user_localpart, accepted):
-            if not user_localpart in self.PRESENCE_LIST:
-                return defer.succeed([])
-            return defer.succeed([
-                {"observed_user_id": u, "accepted": accepted} for u in
-                self.PRESENCE_LIST[user_localpart]])
-        datastore.get_presence_list = get_presence_list
-
-        def is_presence_visible(observed_localpart, observer_userid):
-            return True
-        datastore.is_presence_visible = is_presence_visible
-
-    @defer.inlineCallbacks
-    def setUp_users(self, hs):
-        # Some local users to test with
-        self.u_apple = UserID.from_string("@apple:test")
-        self.u_banana = UserID.from_string("@banana:test")
-        self.u_clementine = UserID.from_string("@clementine:test")
-        self.u_durian = UserID.from_string("@durian:test")
-        self.u_elderberry = UserID.from_string("@elderberry:test")
-        self.u_fig = UserID.from_string("@fig:test")
-
-        # Remote user
-        self.u_onion = UserID.from_string("@onion:farm")
-        self.u_potato = UserID.from_string("@potato:remote")
-
-        yield
-
-
-class PresenceStateTestCase(PresenceTestCase):
-    """ Tests presence management. """
-    @defer.inlineCallbacks
-    def setUp(self):
-        yield super(PresenceStateTestCase, self).setUp()
-
-        self.mock_start = Mock()
-        self.mock_stop = Mock()
-
-        self.handler.start_polling_presence = self.mock_start
-        self.handler.stop_polling_presence = self.mock_stop
-
-    @defer.inlineCallbacks
-    def test_get_my_state(self):
-        state = yield self.handler.get_state(
-            target_user=self.u_apple, auth_user=self.u_apple
-        )
-
-        self.assertEquals(
-            {"presence": ONLINE, "status_msg": "Online"},
-            state
-        )
-
-    @defer.inlineCallbacks
-    def test_get_allowed_state(self):
-        yield self.datastore.allow_presence_visible(
-            observed_localpart=self.u_apple.localpart,
-            observer_userid=self.u_banana.to_string(),
-        )
-
-        state = yield self.handler.get_state(
-            target_user=self.u_apple, auth_user=self.u_banana
-        )
+from synapse.handlers.presence import (
+    handle_update, handle_timeout,
+    IDLE_TIMER, SYNC_ONLINE_TIMEOUT, LAST_ACTIVE_GRANULARITY, FEDERATION_TIMEOUT,
+    FEDERATION_PING_INTERVAL,
+)
+from synapse.storage.presence import UserPresenceState
 
-        self.assertEquals(
-            {"presence": ONLINE, "status_msg": "Online"},
-            state
-        )
 
-    @defer.inlineCallbacks
-    def test_get_same_room_state(self):
-        self.room_members = [self.u_apple, self.u_clementine]
+class PresenceUpdateTestCase(unittest.TestCase):
+    def test_offline_to_online(self):
+        wheel_timer = Mock()
+        user_id = "@foo:bar"
+        now = 5000000
 
-        state = yield self.handler.get_state(
-            target_user=self.u_apple, auth_user=self.u_clementine
+        prev_state = UserPresenceState.default(user_id)
+        new_state = prev_state.copy_and_replace(
+            state=PresenceState.ONLINE,
+            last_active_ts=now,
         )
 
-        self.assertEquals(
-            {"presence": ONLINE, "status_msg": "Online"},
-            state
+        state, persist_and_notify, federation_ping = handle_update(
+            prev_state, new_state, is_mine=True, wheel_timer=wheel_timer, now=now
         )
 
-    @defer.inlineCallbacks
-    def test_get_disallowed_state(self):
-        self.room_members = []
+        self.assertTrue(persist_and_notify)
+        self.assertTrue(state.currently_active)
+        self.assertEquals(new_state.state, state.state)
+        self.assertEquals(new_state.status_msg, state.status_msg)
+        self.assertEquals(state.last_federation_update_ts, now)
 
-        yield self.assertFailure(
-            self.handler.get_state(
-                target_user=self.u_apple, auth_user=self.u_clementine
+        self.assertEquals(wheel_timer.insert.call_count, 3)
+        wheel_timer.insert.assert_has_calls([
+            call(
+                now=now,
+                obj=user_id,
+                then=new_state.last_active_ts + IDLE_TIMER
             ),
-            SynapseError
-        )
-
-    @defer.inlineCallbacks
-    def test_set_my_state(self):
-        yield self.handler.set_state(
-                target_user=self.u_apple, auth_user=self.u_apple,
-                state={"presence": UNAVAILABLE, "status_msg": "Away"})
-
-        self.assertEquals(
-            {"state": UNAVAILABLE,
-             "status_msg": "Away",
-             "mtime": 1000000},
-            (yield self.datastore.get_presence_state(self.u_apple.localpart))
-        )
-
-        self.mock_start.assert_called_with(self.u_apple,
-                state={
-                    "presence": UNAVAILABLE,
-                    "status_msg": "Away",
-                    "last_active": 1000000, # MockClock
-                })
-
-        yield self.handler.set_state(
-                target_user=self.u_apple, auth_user=self.u_apple,
-                state={"presence": OFFLINE})
-
-        self.mock_stop.assert_called_with(self.u_apple)
-
-
-class PresenceInvitesTestCase(PresenceTestCase):
-    """ Tests presence management. """
-    @defer.inlineCallbacks
-    def setUp(self):
-        yield super(PresenceInvitesTestCase, self).setUp()
-
-        self.mock_start = Mock()
-        self.mock_stop = Mock()
-
-        self.handler.start_polling_presence = self.mock_start
-        self.handler.stop_polling_presence = self.mock_stop
-
-    @defer.inlineCallbacks
-    def test_invite_local(self):
-        # TODO(paul): This test will likely break if/when real auth permissions
-        # are added; for now the HS will always accept any invite
-
-        yield self.handler.send_presence_invite(
-                observer_user=self.u_apple, observed_user=self.u_banana)
-
-        self.assertEquals(
-            [{"observed_user_id": "@banana:test", "accepted": 1}],
-            (yield self.datastore.get_presence_list(self.u_apple.localpart))
-        )
-        self.assertTrue(
-            (yield self.datastore.is_presence_visible(
-                observed_localpart=self.u_banana.localpart,
-                observer_userid=self.u_apple.to_string(),
-            ))
-        )
-
-        self.mock_start.assert_called_with(
-                self.u_apple, target_user=self.u_banana)
-
-    @defer.inlineCallbacks
-    def test_invite_local_nonexistant(self):
-        yield self.handler.send_presence_invite(
-                observer_user=self.u_apple, observed_user=self.u_durian)
-
-        self.assertEquals(
-            [],
-            (yield self.datastore.get_presence_list(self.u_apple.localpart))
-        )
-
-    @defer.inlineCallbacks
-    def test_invite_remote(self):
-        # Use a different destination, otherwise retry logic might fail the
-        # request
-        u_rocket = UserID.from_string("@rocket:there")
-
-        put_json = self.mock_http_client.put_json
-        put_json.expect_call_and_return(
-            call("there",
-                path="/_matrix/federation/v1/send/1000000/",
-                data=_expect_edu("there", "m.presence_invite",
-                    content={
-                        "observer_user": "@apple:test",
-                        "observed_user": "@rocket:there",
-                    }
-                ),
-                json_data_callback=ANY,
-                long_retries=True,
+            call(
+                now=now,
+                obj=user_id,
+                then=new_state.last_user_sync_ts + SYNC_ONLINE_TIMEOUT
             ),
-            defer.succeed((200, "OK"))
-        )
+            call(
+                now=now,
+                obj=user_id,
+                then=new_state.last_active_ts + LAST_ACTIVE_GRANULARITY
+            ),
+        ], any_order=True)
 
-        yield self.handler.send_presence_invite(
-                observer_user=self.u_apple, observed_user=u_rocket)
+    def test_online_to_online(self):
+        wheel_timer = Mock()
+        user_id = "@foo:bar"
+        now = 5000000
 
-        self.assertEquals(
-            [{"observed_user_id": "@rocket:there", "accepted": 0}],
-            (yield self.datastore.get_presence_list(self.u_apple.localpart))
+        prev_state = UserPresenceState.default(user_id)
+        prev_state = prev_state.copy_and_replace(
+            state=PresenceState.ONLINE,
+            last_active_ts=now,
+            currently_active=True,
         )
 
-        yield put_json.await_calls()
-
-    @defer.inlineCallbacks
-    def test_accept_remote(self):
-        # TODO(paul): This test will likely break if/when real auth permissions
-        # are added; for now the HS will always accept any invite
-
-        # Use a different destination, otherwise retry logic might fail the
-        # request
-        u_rocket = UserID.from_string("@rocket:moon")
-
-        put_json = self.mock_http_client.put_json
-        put_json.expect_call_and_return(
-            call("moon",
-                path="/_matrix/federation/v1/send/1000000/",
-                data=_expect_edu("moon", "m.presence_accept",
-                    content={
-                        "observer_user": "@rocket:moon",
-                        "observed_user": "@apple:test",
-                    }
-                ),
-                json_data_callback=ANY,
-                long_retries=True,
-            ),
-            defer.succeed((200, "OK"))
+        new_state = prev_state.copy_and_replace(
+            state=PresenceState.ONLINE,
+            last_active_ts=now,
         )
 
-        yield self.mock_federation_resource.trigger("PUT",
-            "/_matrix/federation/v1/send/1000000/",
-            _make_edu_json("elsewhere", "m.presence_invite",
-                content={
-                    "observer_user": "@rocket:moon",
-                    "observed_user": "@apple:test",
-                }
-            )
+        state, persist_and_notify, federation_ping = handle_update(
+            prev_state, new_state, is_mine=True, wheel_timer=wheel_timer, now=now
         )
 
-        self.assertTrue(
-            (yield self.datastore.is_presence_visible(
-                observed_localpart=self.u_apple.localpart,
-                observer_userid=u_rocket.to_string(),
-            ))
-        )
+        self.assertFalse(persist_and_notify)
+        self.assertTrue(federation_ping)
+        self.assertTrue(state.currently_active)
+        self.assertEquals(new_state.state, state.state)
+        self.assertEquals(new_state.status_msg, state.status_msg)
+        self.assertEquals(state.last_federation_update_ts, now)
 
-        yield put_json.await_calls()
-
-    @defer.inlineCallbacks
-    def test_invited_remote_nonexistant(self):
-        # Use a different destination, otherwise retry logic might fail the
-        # request
-        u_rocket = UserID.from_string("@rocket:sun")
-
-        put_json = self.mock_http_client.put_json
-        put_json.expect_call_and_return(
-            call("sun",
-                path="/_matrix/federation/v1/send/1000000/",
-                data=_expect_edu("sun", "m.presence_deny",
-                    content={
-                        "observer_user": "@rocket:sun",
-                        "observed_user": "@durian:test",
-                    }
-                ),
-                json_data_callback=ANY,
-                long_retries=True,
+        self.assertEquals(wheel_timer.insert.call_count, 3)
+        wheel_timer.insert.assert_has_calls([
+            call(
+                now=now,
+                obj=user_id,
+                then=new_state.last_active_ts + IDLE_TIMER
             ),
-            defer.succeed((200, "OK"))
-        )
-
-        yield self.mock_federation_resource.trigger("PUT",
-            "/_matrix/federation/v1/send/1000000/",
-            _make_edu_json("sun", "m.presence_invite",
-                content={
-                    "observer_user": "@rocket:sun",
-                    "observed_user": "@durian:test",
-                }
-            )
-        )
+            call(
+                now=now,
+                obj=user_id,
+                then=new_state.last_user_sync_ts + SYNC_ONLINE_TIMEOUT
+            ),
+            call(
+                now=now,
+                obj=user_id,
+                then=new_state.last_active_ts + LAST_ACTIVE_GRANULARITY
+            ),
+        ], any_order=True)
 
-        yield put_json.await_calls()
+    def test_online_to_online_last_active(self):
+        wheel_timer = Mock()
+        user_id = "@foo:bar"
+        now = 5000000
 
-    @defer.inlineCallbacks
-    def test_accepted_remote(self):
-        yield self.datastore.add_presence_list_pending(
-            observer_localpart=self.u_apple.localpart,
-            observed_userid=self.u_cabbage.to_string(),
+        prev_state = UserPresenceState.default(user_id)
+        prev_state = prev_state.copy_and_replace(
+            state=PresenceState.ONLINE,
+            last_active_ts=now - LAST_ACTIVE_GRANULARITY - 1,
+            currently_active=True,
         )
 
-        yield self.mock_federation_resource.trigger("PUT",
-            "/_matrix/federation/v1/send/1000000/",
-            _make_edu_json("elsewhere", "m.presence_accept",
-                content={
-                    "observer_user": "@apple:test",
-                    "observed_user": "@cabbage:elsewhere",
-                }
-            )
+        new_state = prev_state.copy_and_replace(
+            state=PresenceState.ONLINE,
         )
 
-        self.assertEquals(
-            [{"observed_user_id": "@cabbage:elsewhere", "accepted": 1}],
-            (yield self.datastore.get_presence_list(self.u_apple.localpart))
+        state, persist_and_notify, federation_ping = handle_update(
+            prev_state, new_state, is_mine=True, wheel_timer=wheel_timer, now=now
         )
 
-        self.mock_start.assert_called_with(
-                self.u_apple, target_user=self.u_cabbage)
-
-    @defer.inlineCallbacks
-    def test_denied_remote(self):
-        yield self.datastore.add_presence_list_pending(
-            observer_localpart=self.u_apple.localpart,
-            observed_userid="@eggplant:elsewhere",
-        )
+        self.assertTrue(persist_and_notify)
+        self.assertFalse(state.currently_active)
+        self.assertEquals(new_state.state, state.state)
+        self.assertEquals(new_state.status_msg, state.status_msg)
+        self.assertEquals(state.last_federation_update_ts, now)
 
-        yield self.mock_federation_resource.trigger("PUT",
-            "/_matrix/federation/v1/send/1000000/",
-            _make_edu_json("elsewhere", "m.presence_deny",
-                content={
-                    "observer_user": "@apple:test",
-                    "observed_user": "@eggplant:elsewhere",
-                }
+        self.assertEquals(wheel_timer.insert.call_count, 2)
+        wheel_timer.insert.assert_has_calls([
+            call(
+                now=now,
+                obj=user_id,
+                then=new_state.last_active_ts + IDLE_TIMER
+            ),
+            call(
+                now=now,
+                obj=user_id,
+                then=new_state.last_user_sync_ts + SYNC_ONLINE_TIMEOUT
             )
-        )
-
-        self.assertEquals(
-            [],
-            (yield self.datastore.get_presence_list(self.u_apple.localpart))
-        )
-
-    @defer.inlineCallbacks
-    def test_drop_local(self):
-        yield self.datastore.add_presence_list_pending(
-            observer_localpart=self.u_apple.localpart,
-            observed_userid=self.u_banana.to_string(),
-        )
-        yield self.datastore.set_presence_list_accepted(
-            observer_localpart=self.u_apple.localpart,
-            observed_userid=self.u_banana.to_string(),
-        )
-
-        yield self.handler.drop(
-            observer_user=self.u_apple,
-            observed_user=self.u_banana,
-        )
-
-        self.assertEquals(
-            [],
-            (yield self.datastore.get_presence_list(self.u_apple.localpart))
-        )
-
-        self.mock_stop.assert_called_with(
-                self.u_apple, target_user=self.u_banana)
-
-    @defer.inlineCallbacks
-    def test_drop_remote(self):
-        yield self.datastore.add_presence_list_pending(
-            observer_localpart=self.u_apple.localpart,
-            observed_userid=self.u_cabbage.to_string(),
-        )
-        yield self.datastore.set_presence_list_accepted(
-            observer_localpart=self.u_apple.localpart,
-            observed_userid=self.u_cabbage.to_string(),
-        )
-
-        yield self.handler.drop(
-            observer_user=self.u_apple,
-            observed_user=self.u_cabbage,
-        )
-
-        self.assertEquals(
-            [],
-            (yield self.datastore.get_presence_list(self.u_apple.localpart))
-        )
-
-    @defer.inlineCallbacks
-    def test_get_presence_list(self):
-        yield self.datastore.add_presence_list_pending(
-            observer_localpart=self.u_apple.localpart,
-            observed_userid=self.u_banana.to_string(),
-        )
-        yield self.datastore.set_presence_list_accepted(
-            observer_localpart=self.u_apple.localpart,
-            observed_userid=self.u_banana.to_string(),
-        )
-
-        presence = yield self.handler.get_presence_list(
-                observer_user=self.u_apple)
-
-        self.assertEquals([
-            {"observed_user": self.u_banana,
-             "presence": OFFLINE,
-             "accepted": 1},
-        ], presence)
-
-
-class PresencePushTestCase(MockedDatastorePresenceTestCase):
-    """ Tests steady-state presence status updates.
-
-    They assert that presence state update messages are pushed around the place
-    when users change state, presuming that the watches are all established.
-
-    These tests are MASSIVELY fragile currently as they poke internals of the
-    presence handler; namely the _local_pushmap and _remote_recvmap.
-    BE WARNED...
-    """
-    PRESENCE_LIST = {
-            'apple': [ "@banana:test", "@clementine:test" ],
-            'banana': [ "@apple:test" ],
-    }
-
-    @defer.inlineCallbacks
-    def test_push_local(self):
-        self.room_members = [self.u_apple, self.u_elderberry]
-
-        self.datastore.set_presence_state.return_value = defer.succeed(
-            {"state": ONLINE}
-        )
-
-        # TODO(paul): Gut-wrenching
-        self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
-        self.handler._user_cachemap[self.u_apple].update(
-            {"presence": OFFLINE}, serial=0
-        )
-        apple_set = self.handler._local_pushmap.setdefault("apple", set())
-        apple_set.add(self.u_banana)
-        apple_set.add(self.u_clementine)
-
-        self.assertEquals(self.event_source.get_current_key(), 0)
-
-        yield self.handler.set_state(self.u_apple, self.u_apple,
-            {"presence": ONLINE}
-        )
-
-        # Apple sees self-reflection even without room_id
-        (events, _) = yield self.event_source.get_new_events(
-            user=self.u_apple,
-            from_key=0,
-        )
-
-        self.assertEquals(self.event_source.get_current_key(), 1)
-        self.assertEquals(events,
-            [
-                {"type": "m.presence",
-                 "content": {
-                    "user_id": "@apple:test",
-                    "presence": ONLINE,
-                    "last_active_ago": 0,
-                }},
-            ],
-            msg="Presence event should be visible to self-reflection"
-        )
-
-        # Apple sees self-reflection
-        (events, _) = yield self.event_source.get_new_events(
-            user=self.u_apple,
-            from_key=0,
-            room_ids=[self.room_id],
-        )
-
-        self.assertEquals(self.event_source.get_current_key(), 1)
-        self.assertEquals(events,
-            [
-                {"type": "m.presence",
-                 "content": {
-                    "user_id": "@apple:test",
-                    "presence": ONLINE,
-                    "last_active_ago": 0,
-                }},
-            ],
-            msg="Presence event should be visible to self-reflection"
-        )
-
-        config = SourcePaginationConfig(from_key=1, to_key=0)
-        (chunk, _) = yield self.event_source.get_pagination_rows(
-            self.u_apple, config, None
-        )
-        self.assertEquals(chunk,
-            [
-                {"type": "m.presence",
-                 "content": {
-                     "user_id": "@apple:test",
-                     "presence": ONLINE,
-                     "last_active_ago": 0,
-                }},
-            ]
-        )
-
-        # Banana sees it because of presence subscription
-        (events, _) = yield self.event_source.get_new_events(
-            user=self.u_banana,
-            from_key=0,
-            room_ids=[self.room_id],
-        )
-
-        self.assertEquals(self.event_source.get_current_key(), 1)
-        self.assertEquals(events,
-            [
-                {"type": "m.presence",
-                 "content": {
-                    "user_id": "@apple:test",
-                    "presence": ONLINE,
-                    "last_active_ago": 0,
-                }},
-            ],
-            msg="Presence event should be visible to explicit subscribers"
-        )
-
-        # Elderberry sees it because of same room
-        (events, _) = yield self.event_source.get_new_events(
-            user=self.u_elderberry,
-            from_key=0,
-            room_ids=[self.room_id],
-        )
-
-        self.assertEquals(self.event_source.get_current_key(), 1)
-        self.assertEquals(events,
-            [
-                {"type": "m.presence",
-                 "content": {
-                    "user_id": "@apple:test",
-                    "presence": ONLINE,
-                    "last_active_ago": 0,
-                }},
-            ],
-            msg="Presence event should be visible to other room members"
-        )
-
-        # Durian is not in the room, should not see this event
-        (events, _) = yield self.event_source.get_new_events(
-            user=self.u_durian,
-            from_key=0,
-            room_ids=[],
-        )
+        ], any_order=True)
 
-        self.assertEquals(self.event_source.get_current_key(), 1)
-        self.assertEquals(events, [],
-            msg="Presence event should not be visible to others"
-        )
+    def test_remote_ping_timer(self):
+        wheel_timer = Mock()
+        user_id = "@foo:bar"
+        now = 5000000
 
-        presence = yield self.handler.get_presence_list(
-                observer_user=self.u_apple, accepted=True)
-
-        self.assertEquals(
-            [
-                {"observed_user": self.u_banana,
-                 "presence": OFFLINE,
-                 "accepted": True},
-                {"observed_user": self.u_clementine,
-                 "presence": OFFLINE,
-                 "accepted": True},
-            ],
-            presence
+        prev_state = UserPresenceState.default(user_id)
+        prev_state = prev_state.copy_and_replace(
+            state=PresenceState.ONLINE,
+            last_active_ts=now,
         )
 
-        # TODO(paul): Gut-wrenching
-        banana_set = self.handler._local_pushmap.setdefault("banana", set())
-        banana_set.add(self.u_apple)
-
-        yield self.handler.set_state(self.u_banana, self.u_banana,
-            {"presence": ONLINE}
+        new_state = prev_state.copy_and_replace(
+            state=PresenceState.ONLINE,
         )
 
-        self.clock.advance_time(2)
-
-        presence = yield self.handler.get_presence_list(
-                observer_user=self.u_apple, accepted=True)
-
-        self.assertEquals([
-                {"observed_user": self.u_banana,
-                 "presence": ONLINE,
-                 "last_active_ago": 2000,
-                 "accepted": True},
-                {"observed_user": self.u_clementine,
-                 "presence": OFFLINE,
-                 "accepted": True},
-        ], presence)
-
-        (events, _) = yield self.event_source.get_new_events(
-            user=self.u_apple,
-            from_key=1,
+        state, persist_and_notify, federation_ping = handle_update(
+            prev_state, new_state, is_mine=False, wheel_timer=wheel_timer, now=now
         )
 
-        self.assertEquals(self.event_source.get_current_key(), 2)
-        self.assertEquals(events,
-            [
-                {"type": "m.presence",
-                 "content": {
-                     "user_id": "@banana:test",
-                     "presence": ONLINE,
-                     "last_active_ago": 2000
-                }},
-            ]
-        )
+        self.assertFalse(persist_and_notify)
+        self.assertFalse(federation_ping)
+        self.assertFalse(state.currently_active)
+        self.assertEquals(new_state.state, state.state)
+        self.assertEquals(new_state.status_msg, state.status_msg)
 
-    @defer.inlineCallbacks
-    def test_push_remote(self):
-        put_json = self.mock_http_client.put_json
-        put_json.expect_call_and_return(
-            call("farm",
-                path=ANY,  # Can't guarantee which txn ID will be which
-                data=_expect_edu("farm", "m.presence",
-                    content={
-                        "push": [
-                            {"user_id": "@apple:test",
-                             "presence": u"online",
-                             "last_active_ago": 0},
-                        ],
-                    }
-                ),
-                json_data_callback=ANY,
-                long_retries=True,
+        self.assertEquals(wheel_timer.insert.call_count, 1)
+        wheel_timer.insert.assert_has_calls([
+            call(
+                now=now,
+                obj=user_id,
+                then=new_state.last_federation_update_ts + FEDERATION_TIMEOUT
             ),
-            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",
-                             "presence": u"online",
-                             "last_active_ago": 0},
-                        ],
-                    }
-                ),
-                json_data_callback=ANY,
-                long_retries=True,
-            ),
-            defer.succeed((200, "OK"))
-        )
+        ], any_order=True)
 
-        self.room_members = [self.u_apple, self.u_onion]
+    def test_online_to_offline(self):
+        wheel_timer = Mock()
+        user_id = "@foo:bar"
+        now = 5000000
 
-        self.datastore.set_presence_state.return_value = defer.succeed(
-            {"state": ONLINE}
+        prev_state = UserPresenceState.default(user_id)
+        prev_state = prev_state.copy_and_replace(
+            state=PresenceState.ONLINE,
+            last_active_ts=now,
+            currently_active=True,
         )
 
-        # TODO(paul): Gut-wrenching
-        self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
-        self.handler._user_cachemap[self.u_apple].update(
-            {"presence": OFFLINE}, serial=0
+        new_state = prev_state.copy_and_replace(
+            state=PresenceState.OFFLINE,
         )
-        apple_set = self.handler._remote_sendmap.setdefault("apple", set())
-        apple_set.add(self.u_potato.domain)
 
-        yield self.handler.set_state(self.u_apple, self.u_apple,
-            {"presence": ONLINE}
+        state, persist_and_notify, federation_ping = handle_update(
+            prev_state, new_state, is_mine=True, wheel_timer=wheel_timer, now=now
         )
 
-        yield put_json.await_calls()
+        self.assertTrue(persist_and_notify)
+        self.assertEquals(new_state.state, state.state)
+        self.assertEquals(state.last_federation_update_ts, now)
 
-    @defer.inlineCallbacks
-    def test_recv_remote(self):
-        self.room_members = [self.u_apple, self.u_banana, self.u_potato]
+        self.assertEquals(wheel_timer.insert.call_count, 0)
 
-        self.assertEquals(self.event_source.get_current_key(), 0)
-
-        yield self.mock_federation_resource.trigger("PUT",
-            "/_matrix/federation/v1/send/1000000/",
-            _make_edu_json("elsewhere", "m.presence",
-                content={
-                    "push": [
-                        {"user_id": "@potato:remote",
-                         "presence": "online",
-                         "last_active_ago": 1000},
-                    ],
-                }
-            )
-        )
+    def test_online_to_idle(self):
+        wheel_timer = Mock()
+        user_id = "@foo:bar"
+        now = 5000000
 
-        (events, _) = yield self.event_source.get_new_events(
-            user=self.u_apple,
-            from_key=0,
-            room_ids=[self.room_id],
+        prev_state = UserPresenceState.default(user_id)
+        prev_state = prev_state.copy_and_replace(
+            state=PresenceState.ONLINE,
+            last_active_ts=now,
+            currently_active=True,
         )
 
-        self.assertEquals(self.event_source.get_current_key(), 1)
-        self.assertEquals(events,
-            [
-                {"type": "m.presence",
-                 "content": {
-                     "user_id": "@potato:remote",
-                     "presence": ONLINE,
-                     "last_active_ago": 1000,
-                }}
-            ]
+        new_state = prev_state.copy_and_replace(
+            state=PresenceState.UNAVAILABLE,
         )
 
-        self.clock.advance_time(2)
-
-        state = yield self.handler.get_state(self.u_potato, self.u_apple)
-
-        self.assertEquals(
-            {"presence": ONLINE, "last_active_ago": 3000},
-            state
+        state, persist_and_notify, federation_ping = handle_update(
+            prev_state, new_state, is_mine=True, wheel_timer=wheel_timer, now=now
         )
 
-    @defer.inlineCallbacks
-    def test_recv_remote_offline(self):
-        """ Various tests relating to SYN-261 """
-
-        self.room_members = [self.u_apple, self.u_banana, self.u_potato]
+        self.assertTrue(persist_and_notify)
+        self.assertEquals(new_state.state, state.state)
+        self.assertEquals(state.last_federation_update_ts, now)
+        self.assertEquals(new_state.state, state.state)
+        self.assertEquals(new_state.status_msg, state.status_msg)
 
-        self.assertEquals(self.event_source.get_current_key(), 0)
-
-        yield self.mock_federation_resource.trigger("PUT",
-            "/_matrix/federation/v1/send/1000000/",
-            _make_edu_json("elsewhere", "m.presence",
-                content={
-                    "push": [
-                        {"user_id": "@potato:remote",
-                         "presence": "offline"},
-                    ],
-                }
+        self.assertEquals(wheel_timer.insert.call_count, 1)
+        wheel_timer.insert.assert_has_calls([
+            call(
+                now=now,
+                obj=user_id,
+                then=new_state.last_user_sync_ts + SYNC_ONLINE_TIMEOUT
             )
-        )
-
-        self.assertEquals(self.event_source.get_current_key(), 1)
-
-        (events, _) = yield self.event_source.get_new_events(
-            user=self.u_apple,
-            from_key=0,
-            room_ids=[self.room_id,]
-        )
-        self.assertEquals(events,
-            [
-                {"type": "m.presence",
-                 "content": {
-                     "user_id": "@potato:remote",
-                     "presence": OFFLINE,
-                }}
-            ]
-        )
-
-        yield self.mock_federation_resource.trigger("PUT",
-            "/_matrix/federation/v1/send/1000001/",
-            _make_edu_json("elsewhere", "m.presence",
-                content={
-                    "push": [
-                        {"user_id": "@potato:remote",
-                         "presence": "online"},
-                    ],
-                }
-            )
-        )
-
-        self.assertEquals(self.event_source.get_current_key(), 2)
-
-        (events, _) = yield self.event_source.get_new_events(
-            user=self.u_apple,
-            from_key=0,
-            room_ids=[self.room_id,]
-        )
-        self.assertEquals(events,
-            [
-                {"type": "m.presence",
-                 "content": {
-                     "user_id": "@potato:remote",
-                     "presence": ONLINE,
-                }}
-            ]
-        )
-
-    @defer.inlineCallbacks
-    def test_join_room_local(self):
-        self.room_members = [self.u_apple, self.u_banana]
-
-        self.assertEquals(self.event_source.get_current_key(), 0)
-
-        # TODO(paul): Gut-wrenching
-        self.handler._user_cachemap[self.u_clementine] = UserPresenceCache()
-        self.handler._user_cachemap[self.u_clementine].update(
-            {
-                "presence": PresenceState.ONLINE,
-                "last_active": self.clock.time_msec(),
-            }, self.u_clementine
-        )
-
-        yield self.distributor.fire("user_joined_room", self.u_clementine,
-            self.room_id
-        )
+        ], any_order=True)
 
-        self.room_members.append(self.u_clementine)
 
-        (events, _) = yield self.event_source.get_new_events(
-            user=self.u_apple,
-            from_key=0,
-        )
+class PresenceTimeoutTestCase(unittest.TestCase):
+    def test_idle_timer(self):
+        user_id = "@foo:bar"
+        now = 5000000
 
-        self.assertEquals(self.event_source.get_current_key(), 1)
-        self.assertEquals(events,
-            [
-                {"type": "m.presence",
-                 "content": {
-                     "user_id": "@clementine:test",
-                     "presence": ONLINE,
-                     "last_active_ago": 0,
-                }}
-            ]
+        state = UserPresenceState.default(user_id)
+        state = state.copy_and_replace(
+            state=PresenceState.ONLINE,
+            last_active_ts=now - IDLE_TIMER - 1,
+            last_user_sync_ts=now,
         )
 
-    @defer.inlineCallbacks
-    def test_join_room_remote(self):
-        ## Sending local user state to a newly-joined remote user
-        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",
-                             "presence": "online"},
-                        ],
-                    }
-                ),
-                json_data_callback=ANY,
-                long_retries=True,
-            ),
-            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": "@banana:test",
-                             "presence": "offline"},
-                        ],
-                    }
-                ),
-                json_data_callback=ANY,
-                long_retries=True,
-            ),
-            defer.succeed((200, "OK"))
+        new_state = handle_timeout(
+            state, is_mine=True, user_to_num_current_syncs={}, now=now
         )
 
-        # TODO(paul): Gut-wrenching
-        self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
-        self.handler._user_cachemap[self.u_apple].update(
-                {"presence": PresenceState.ONLINE}, self.u_apple)
-        self.room_members = [self.u_apple, self.u_banana]
+        self.assertIsNotNone(new_state)
+        self.assertEquals(new_state.state, PresenceState.UNAVAILABLE)
 
-        yield self.distributor.fire("user_joined_room", self.u_potato,
-            self.room_id
-        )
+    def test_sync_timeout(self):
+        user_id = "@foo:bar"
+        now = 5000000
 
-        yield put_json.await_calls()
-
-        ## Sending newly-joined local user state to remote users
-
-        put_json.expect_call_and_return(
-            call("remote",
-                path="/_matrix/federation/v1/send/1000002/",
-                data=_expect_edu("remote", "m.presence",
-                    content={
-                        "push": [
-                            {"user_id": "@clementine:test",
-                             "presence": "online"},
-                        ],
-                    }
-                ),
-                json_data_callback=ANY,
-                long_retries=True,
-            ),
-            defer.succeed((200, "OK"))
+        state = UserPresenceState.default(user_id)
+        state = state.copy_and_replace(
+            state=PresenceState.ONLINE,
+            last_active_ts=now,
+            last_user_sync_ts=now - SYNC_ONLINE_TIMEOUT - 1,
         )
 
-        self.handler._user_cachemap[self.u_clementine] = UserPresenceCache()
-        self.handler._user_cachemap[self.u_clementine].update(
-                {"presence": ONLINE}, self.u_clementine)
-        self.room_members.append(self.u_potato)
-
-        yield self.distributor.fire("user_joined_room", self.u_clementine,
-            self.room_id
+        new_state = handle_timeout(
+            state, is_mine=True, user_to_num_current_syncs={}, now=now
         )
 
-        put_json.await_calls()
-
-
-class PresencePollingTestCase(MockedDatastorePresenceTestCase):
-    """ Tests presence status polling. """
-
-    # For this test, we have three local users; apple is watching and is
-    # watched by the other two, but the others don't watch each other.
-    # Additionally clementine is watching a remote user.
-    PRESENCE_LIST = {
-            'apple': [ "@banana:test", "@clementine:test" ],
-            'banana': [ "@apple:test" ],
-            'clementine': [ "@apple:test", "@potato:remote" ],
-            'fig': [ "@potato:remote" ],
-    }
-
-    @defer.inlineCallbacks
-    def setUp(self):
-        yield super(PresencePollingTestCase, self).setUp()
-
-        self.mock_update_client = Mock()
+        self.assertIsNotNone(new_state)
+        self.assertEquals(new_state.state, PresenceState.OFFLINE)
 
-        def update(*args,**kwargs):
-            return defer.succeed(None)
-        self.mock_update_client.side_effect = update
+    def test_sync_online(self):
+        user_id = "@foo:bar"
+        now = 5000000
 
-        self.handler.push_update_to_clients = self.mock_update_client
-
-    @defer.inlineCallbacks
-    def test_push_local(self):
-        # apple goes online
-        yield self.handler.set_state(
-            target_user=self.u_apple, auth_user=self.u_apple,
-            state={"presence": ONLINE}
+        state = UserPresenceState.default(user_id)
+        state = state.copy_and_replace(
+            state=PresenceState.ONLINE,
+            last_active_ts=now - SYNC_ONLINE_TIMEOUT - 1,
+            last_user_sync_ts=now - SYNC_ONLINE_TIMEOUT - 1,
         )
 
-        # apple should see both banana and clementine currently offline
-        self.mock_update_client.assert_has_calls([
-            call(users_to_push=[self.u_apple]),
-            call(users_to_push=[self.u_apple]),
-        ], any_order=True)
-
-        # Gut-wrenching tests
-        self.assertTrue("banana" in self.handler._local_pushmap)
-        self.assertTrue(self.u_apple in self.handler._local_pushmap["banana"])
-        self.assertTrue("clementine" in self.handler._local_pushmap)
-        self.assertTrue(self.u_apple in self.handler._local_pushmap["clementine"])
-
-        self.mock_update_client.reset_mock()
-
-        # banana goes online
-        yield self.handler.set_state(
-            target_user=self.u_banana, auth_user=self.u_banana,
-            state={"presence": ONLINE}
+        new_state = handle_timeout(
+            state, is_mine=True, user_to_num_current_syncs={
+                user_id: 1,
+            }, now=now
         )
 
-        # apple and banana should now both see each other online
-        self.mock_update_client.assert_has_calls([
-            call(users_to_push=set([self.u_apple]), room_ids=[]),
-            call(users_to_push=[self.u_banana]),
-        ], any_order=True)
+        self.assertIsNotNone(new_state)
+        self.assertEquals(new_state.state, PresenceState.ONLINE)
 
-        self.assertTrue("apple" in self.handler._local_pushmap)
-        self.assertTrue(self.u_banana in self.handler._local_pushmap["apple"])
+    def test_federation_ping(self):
+        user_id = "@foo:bar"
+        now = 5000000
 
-        self.mock_update_client.reset_mock()
-
-        # apple goes offline
-        yield self.handler.set_state(
-            target_user=self.u_apple, auth_user=self.u_apple,
-            state={"presence": OFFLINE}
-        )
-
-        # banana should now be told apple is offline
-        self.mock_update_client.assert_has_calls([
-            call(users_to_push=set([self.u_banana, self.u_apple]), room_ids=[]),
-        ], any_order=True)
-
-        self.assertFalse("banana" in self.handler._local_pushmap)
-        self.assertFalse("clementine" in self.handler._local_pushmap)
-
-    @defer.inlineCallbacks
-    def test_remote_poll_send(self):
-        put_json = self.mock_http_client.put_json
-        put_json.expect_call_and_return(
-            call("remote",
-                path=ANY,
-                data=_expect_edu("remote", "m.presence",
-                    content={
-                        "poll": [ "@potato:remote" ],
-                    },
-                ),
-                json_data_callback=ANY,
-                long_retries=True,
-            ),
-            defer.succeed((200, "OK"))
+        state = UserPresenceState.default(user_id)
+        state = state.copy_and_replace(
+            state=PresenceState.ONLINE,
+            last_active_ts=now,
+            last_user_sync_ts=now,
+            last_federation_update_ts=now - FEDERATION_PING_INTERVAL - 1,
         )
 
-        put_json.expect_call_and_return(
-            call("remote",
-                path=ANY,
-                data=_expect_edu("remote", "m.presence",
-                    content={
-                        "push": [ {
-                            "user_id": "@clementine:test",
-                            "presence": OFFLINE,
-                        }],
-                    },
-                ),
-                json_data_callback=ANY,
-                long_retries=True,
-            ),
-            defer.succeed((200, "OK"))
+        new_state = handle_timeout(
+            state, is_mine=True, user_to_num_current_syncs={}, now=now
         )
 
-        # clementine goes online
-        yield self.handler.set_state(
-            target_user=self.u_clementine, auth_user=self.u_clementine,
-            state={"presence": ONLINE}
-        )
+        self.assertIsNotNone(new_state)
+        self.assertEquals(new_state, new_state)
 
-        yield put_json.await_calls()
+    def test_no_timeout(self):
+        user_id = "@foo:bar"
+        now = 5000000
 
-        # Gut-wrenching tests
-        self.assertTrue(self.u_potato in self.handler._remote_recvmap,
-            msg="expected potato to be in _remote_recvmap"
-        )
-        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",
-                            "presence": OFFLINE,
-                        }],
-                    },
-                ),
-                json_data_callback=ANY,
-                long_retries=True,
-            ),
-            defer.succeed((200, "OK"))
+        state = UserPresenceState.default(user_id)
+        state = state.copy_and_replace(
+            state=PresenceState.ONLINE,
+            last_active_ts=now,
+            last_user_sync_ts=now,
+            last_federation_update_ts=now,
         )
 
-        # 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={"presence": ONLINE}
+        new_state = handle_timeout(
+            state, is_mine=True, user_to_num_current_syncs={}, now=now
         )
 
-        # reactor.iterate(delay=0)
+        self.assertIsNone(new_state)
 
-        yield put_json.await_calls()
+    def test_federation_timeout(self):
+        user_id = "@foo:bar"
+        now = 5000000
 
-        # fig goes offline
-        yield self.handler.set_state(
-            target_user=self.u_fig, auth_user=self.u_fig,
-            state={"presence": OFFLINE}
+        state = UserPresenceState.default(user_id)
+        state = state.copy_and_replace(
+            state=PresenceState.ONLINE,
+            last_active_ts=now,
+            last_user_sync_ts=now,
+            last_federation_update_ts=now - FEDERATION_TIMEOUT - 1,
         )
 
-        reactor.iterate(delay=0)
-
-        put_json.assert_had_no_calls()
-
-        put_json.expect_call_and_return(
-            call("remote",
-                path=ANY,
-                data=_expect_edu("remote", "m.presence",
-                    content={
-                        "unpoll": [ "@potato:remote" ],
-                    },
-                ),
-                json_data_callback=ANY,
-                long_retries=True,
-            ),
-            defer.succeed((200, "OK"))
-        )
-
-        # clementine goes offline
-        yield self.handler.set_state(
-            target_user=self.u_clementine, auth_user=self.u_clementine,
-            state={"presence": OFFLINE}
+        new_state = handle_timeout(
+            state, is_mine=False, user_to_num_current_syncs={}, now=now
         )
 
-        yield put_json.await_calls()
+        self.assertIsNotNone(new_state)
+        self.assertEquals(new_state.state, PresenceState.OFFLINE)
 
-        self.assertFalse(self.u_potato in self.handler._remote_recvmap,
-            msg="expected potato not to be in _remote_recvmap"
-        )
+    def test_last_active(self):
+        user_id = "@foo:bar"
+        now = 5000000
 
-    @defer.inlineCallbacks
-    def test_remote_poll_receive(self):
-        put_json = self.mock_http_client.put_json
-        put_json.expect_call_and_return(
-            call("remote",
-                path="/_matrix/federation/v1/send/1000000/",
-                data=_expect_edu("remote", "m.presence",
-                    content={
-                        "push": [
-                            {"user_id": "@banana:test",
-                             "presence": "offline",
-                             "status_msg": None},
-                        ],
-                    },
-                ),
-                json_data_callback=ANY,
-                long_retries=True,
-            ),
-            defer.succeed((200, "OK"))
+        state = UserPresenceState.default(user_id)
+        state = state.copy_and_replace(
+            state=PresenceState.ONLINE,
+            last_active_ts=now - LAST_ACTIVE_GRANULARITY - 1,
+            last_user_sync_ts=now,
+            last_federation_update_ts=now,
         )
 
-        yield self.mock_federation_resource.trigger("PUT",
-            "/_matrix/federation/v1/send/1000000/",
-            _make_edu_json("remote", "m.presence",
-                content={
-                    "poll": [ "@banana:test" ],
-                },
-            )
-        )
-
-        yield put_json.await_calls()
-
-        # Gut-wrenching tests
-        self.assertTrue(self.u_banana in self.handler._remote_sendmap)
-
-        yield self.mock_federation_resource.trigger("PUT",
-            "/_matrix/federation/v1/send/1000001/",
-            _make_edu_json("remote", "m.presence",
-                content={
-                    "unpoll": [ "@banana:test" ],
-                }
-            )
+        new_state = handle_timeout(
+            state, is_mine=True, user_to_num_current_syncs={}, now=now
         )
 
-        # Gut-wrenching tests
-        self.assertFalse(self.u_banana in self.handler._remote_sendmap)
+        self.assertIsNotNone(new_state)
+        self.assertEquals(state, new_state)
diff --git a/tests/handlers/test_presencelike.py b/tests/handlers/test_presencelike.py
deleted file mode 100644
index 76f6ba5e7b..0000000000
--- a/tests/handlers/test_presencelike.py
+++ /dev/null
@@ -1,311 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""This file contains tests of the "presence-like" data that is shared between
-presence and profiles; namely, the displayname and avatar_url."""
-
-from tests import unittest
-from twisted.internet import defer
-
-from mock import Mock, call, ANY, NonCallableMock
-
-from ..utils import MockClock, setup_test_homeserver
-
-from synapse.api.constants import PresenceState
-from synapse.handlers.presence import PresenceHandler
-from synapse.handlers.profile import ProfileHandler
-from synapse.types import UserID
-
-
-OFFLINE = PresenceState.OFFLINE
-UNAVAILABLE = PresenceState.UNAVAILABLE
-ONLINE = PresenceState.ONLINE
-
-
-class MockReplication(object):
-    def __init__(self):
-        self.edu_handlers = {}
-
-    def register_edu_handler(self, edu_type, handler):
-        self.edu_handlers[edu_type] = handler
-
-    def register_query_handler(self, query_type, handler):
-        pass
-
-    def received_edu(self, origin, edu_type, content):
-        self.edu_handlers[edu_type](origin, content)
-
-
-class PresenceAndProfileHandlers(object):
-    def __init__(self, hs):
-        self.presence_handler = PresenceHandler(hs)
-        self.profile_handler = ProfileHandler(hs)
-
-
-class PresenceProfilelikeDataTestCase(unittest.TestCase):
-
-    @defer.inlineCallbacks
-    def setUp(self):
-        hs = yield setup_test_homeserver(
-            clock=MockClock(),
-            datastore=Mock(spec=[
-                "set_presence_state",
-                "is_presence_visible",
-                "set_profile_displayname",
-                "get_rooms_for_user",
-            ]),
-            handlers=None,
-            resource_for_federation=Mock(),
-            http_client=None,
-            replication_layer=MockReplication(),
-            ratelimiter=NonCallableMock(spec_set=[
-                "send_message",
-            ]),
-        )
-        self.ratelimiter = hs.get_ratelimiter()
-        self.ratelimiter.send_message.return_value = (True, 0)
-        hs.handlers = PresenceAndProfileHandlers(hs)
-
-        self.datastore = hs.get_datastore()
-
-        self.replication = hs.get_replication_layer()
-        self.replication.send_edu = Mock()
-
-        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")
-        self.datastore.get_profile_displayname = get_profile_displayname
-
-        def is_presence_visible(*args, **kwargs):
-            return defer.succeed(False)
-        self.datastore.is_presence_visible = is_presence_visible
-
-        def get_profile_avatar_url(user_localpart):
-            return defer.succeed("http://foo")
-        self.datastore.get_profile_avatar_url = get_profile_avatar_url
-
-        self.presence_list = [
-            {"observed_user_id": "@banana:test", "accepted": True},
-            {"observed_user_id": "@clementine:test", "accepted": True},
-        ]
-        def get_presence_list(user_localpart, accepted=None):
-            return defer.succeed(self.presence_list)
-        self.datastore.get_presence_list = get_presence_list
-
-        def user_rooms_intersect(userlist):
-            return defer.succeed(False)
-        self.datastore.user_rooms_intersect = user_rooms_intersect
-
-        self.handlers = hs.get_handlers()
-
-        self.mock_update_client = Mock()
-        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)
-
-        hs.handlers.room_member_handler = Mock(spec=[
-            "get_joined_rooms_for_user",
-        ])
-        hs.handlers.room_member_handler.get_joined_rooms_for_user = (
-                lambda u: defer.succeed([]))
-
-        # Some local users to test with
-        self.u_apple = UserID.from_string("@apple:test")
-        self.u_banana = UserID.from_string("@banana:test")
-        self.u_clementine = UserID.from_string("@clementine:test")
-
-        # Remote user
-        self.u_potato = UserID.from_string("@potato:remote")
-
-        self.mock_get_joined = (
-            self.datastore.get_rooms_for_user
-        )
-
-    @defer.inlineCallbacks
-    def test_set_my_state(self):
-        self.presence_list = [
-            {"observed_user_id": "@banana:test", "accepted": True},
-            {"observed_user_id": "@clementine:test", "accepted": True},
-        ]
-
-        mocked_set = self.datastore.set_presence_state
-        mocked_set.return_value = defer.succeed({"state": OFFLINE})
-
-        yield self.handlers.presence_handler.set_state(
-                target_user=self.u_apple, auth_user=self.u_apple,
-                state={"presence": UNAVAILABLE, "status_msg": "Away"})
-
-        mocked_set.assert_called_with("apple",
-            {"state": UNAVAILABLE, "status_msg": "Away"}
-        )
-
-    @defer.inlineCallbacks
-    def test_push_local(self):
-        def get_joined(*args):
-            return defer.succeed([])
-
-        self.mock_get_joined.side_effect = get_joined
-
-        self.presence_list = [
-            {"observed_user_id": "@banana:test", "accepted": True},
-            {"observed_user_id": "@clementine:test", "accepted": True},
-        ]
-
-        self.datastore.set_presence_state.return_value = defer.succeed(
-            {"state": ONLINE}
-        )
-
-        # TODO(paul): Gut-wrenching
-        from synapse.handlers.presence import UserPresenceCache
-        self.handlers.presence_handler._user_cachemap[self.u_apple] = (
-            UserPresenceCache()
-        )
-        self.handlers.presence_handler._user_cachemap[self.u_apple].update(
-            {"presence": OFFLINE}, serial=0
-        )
-        apple_set = self.handlers.presence_handler._local_pushmap.setdefault(
-                "apple", set())
-        apple_set.add(self.u_banana)
-        apple_set.add(self.u_clementine)
-
-        yield self.handlers.presence_handler.set_state(self.u_apple,
-            self.u_apple, {"presence": ONLINE}
-        )
-        yield self.handlers.presence_handler.set_state(self.u_banana,
-            self.u_banana, {"presence": ONLINE}
-        )
-
-        presence = yield self.handlers.presence_handler.get_presence_list(
-                observer_user=self.u_apple, accepted=True)
-
-        self.assertEquals([
-            {"observed_user": self.u_banana,
-                "presence": ONLINE,
-                "last_active_ago": 0,
-                "displayname": "Frank",
-                "avatar_url": "http://foo",
-                "accepted": True},
-            {"observed_user": self.u_clementine,
-                "presence": OFFLINE,
-                "accepted": True}
-        ], presence)
-
-        self.mock_update_client.assert_has_calls([
-            call(
-                users_to_push={self.u_apple, self.u_banana, self.u_clementine},
-                room_ids=[]
-            ),
-        ], any_order=True)
-
-        self.mock_update_client.reset_mock()
-
-        self.datastore.set_profile_displayname.return_value = defer.succeed(
-                None)
-
-        yield self.handlers.profile_handler.set_displayname(self.u_apple,
-                self.u_apple, "I am an Apple")
-
-        self.mock_update_client.assert_has_calls([
-            call(
-                users_to_push={self.u_apple, self.u_banana, self.u_clementine},
-                room_ids=[],
-            ),
-        ], any_order=True)
-
-    @defer.inlineCallbacks
-    def test_push_remote(self):
-        self.presence_list = [
-            {"observed_user_id": "@potato:remote", "accepted": True},
-        ]
-
-        self.datastore.set_presence_state.return_value = defer.succeed(
-            {"state": ONLINE}
-        )
-
-        # TODO(paul): Gut-wrenching
-        from synapse.handlers.presence import UserPresenceCache
-        self.handlers.presence_handler._user_cachemap[self.u_apple] = (
-            UserPresenceCache()
-        )
-        self.handlers.presence_handler._user_cachemap[self.u_apple].update(
-            {"presence": OFFLINE}, serial=0
-        )
-        apple_set = self.handlers.presence_handler._remote_sendmap.setdefault(
-                "apple", set())
-        apple_set.add(self.u_potato.domain)
-
-        yield self.handlers.presence_handler.set_state(self.u_apple,
-            self.u_apple, {"presence": ONLINE}
-        )
-
-        self.replication.send_edu.assert_called_with(
-                destination="remote",
-                edu_type="m.presence",
-                content={
-                    "push": [
-                        {"user_id": "@apple:test",
-                         "presence": "online",
-                         "last_active_ago": 0,
-                         "displayname": "Frank",
-                         "avatar_url": "http://foo"},
-                    ],
-                },
-        )
-
-    @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()
-        )
-        potato_set.add(self.u_apple)
-
-        yield self.replication.received_edu(
-            "remote", "m.presence", {
-                "push": [
-                    {"user_id": "@potato:remote",
-                     "presence": "online",
-                     "displayname": "Frank",
-                     "avatar_url": "http://foo"},
-                ],
-            }
-        )
-
-        self.mock_update_client.assert_called_with(
-            users_to_push=set([self.u_apple]),
-            room_ids=[],
-        )
-
-        state = yield self.handlers.presence_handler.get_state(self.u_potato,
-                self.u_apple)
-
-        self.assertEquals(
-                {"presence": ONLINE,
-                 "displayname": "Frank",
-                 "avatar_url": "http://foo"},
-            state)
diff --git a/tests/handlers/test_profile.py b/tests/handlers/test_profile.py
index 237fc8223c..a87703bbfd 100644
--- a/tests/handlers/test_profile.py
+++ b/tests/handlers/test_profile.py
@@ -41,8 +41,10 @@ class ProfileTestCase(unittest.TestCase):
         ])
 
         self.query_handlers = {}
+
         def register_query_handler(query_type, handler):
             self.query_handlers[query_type] = handler
+
         self.mock_federation.register_query_handler = register_query_handler
 
         hs = yield setup_test_homeserver(
@@ -63,16 +65,13 @@ class ProfileTestCase(unittest.TestCase):
         self.store = hs.get_datastore()
 
         self.frank = UserID.from_string("@1234ABCD:test")
-        self.bob   = UserID.from_string("@4567:test")
+        self.bob = UserID.from_string("@4567:test")
         self.alice = UserID.from_string("@alice:remote")
 
         yield self.store.create_profile(self.frank.localpart)
 
         self.handler = hs.get_handlers().profile_handler
 
-        # TODO(paul): Icky signal declarings.. booo
-        hs.get_distributor().declare("changed_presencelike_data")
-
     @defer.inlineCallbacks
     def test_get_my_name(self):
         yield self.store.set_profile_displayname(
@@ -136,8 +135,9 @@ class ProfileTestCase(unittest.TestCase):
 
     @defer.inlineCallbacks
     def test_set_my_avatar(self):
-        yield self.handler.set_avatar_url(self.frank, self.frank,
-                "http://my.server/pic.gif")
+        yield self.handler.set_avatar_url(
+            self.frank, self.frank, "http://my.server/pic.gif"
+        )
 
         self.assertEquals(
             (yield self.store.get_profile_avatar_url(self.frank.localpart)),
diff --git a/tests/handlers/test_typing.py b/tests/handlers/test_typing.py
index 763c04d667..3955e7f5b1 100644
--- a/tests/handlers/test_typing.py
+++ b/tests/handlers/test_typing.py
@@ -138,9 +138,9 @@ class TypingNotificationsTestCase(unittest.TestCase):
         self.room_member_handler.get_joined_rooms_for_user = get_joined_rooms_for_user
 
         @defer.inlineCallbacks
-        def fetch_room_distributions_into(room_id, localusers=None,
-                remotedomains=None, ignore_user=None):
-
+        def fetch_room_distributions_into(
+            room_id, localusers=None, remotedomains=None, ignore_user=None
+        ):
             members = yield get_room_members(room_id)
             for member in members:
                 if ignore_user is not None and member == ignore_user:
@@ -153,7 +153,8 @@ class TypingNotificationsTestCase(unittest.TestCase):
                     if remotedomains is not None:
                         remotedomains.add(member.domain)
         self.room_member_handler.fetch_room_distributions_into = (
-                fetch_room_distributions_into)
+            fetch_room_distributions_into
+        )
 
         def check_joined_room(room_id, user_id):
             if user_id not in [u.to_string() for u in self.room_members]:
@@ -207,9 +208,12 @@ class TypingNotificationsTestCase(unittest.TestCase):
 
         put_json = self.mock_http_client.put_json
         put_json.expect_call_and_return(
-            call("farm",
+            call(
+                "farm",
                 path="/_matrix/federation/v1/send/1000000/",
-                data=_expect_edu("farm", "m.typing",
+                data=_expect_edu(
+                    "farm",
+                    "m.typing",
                     content={
                         "room_id": self.room_id,
                         "user_id": self.u_apple.to_string(),
@@ -237,9 +241,12 @@ class TypingNotificationsTestCase(unittest.TestCase):
 
         self.assertEquals(self.event_source.get_current_key(), 0)
 
-        yield self.mock_federation_resource.trigger("PUT",
+        yield self.mock_federation_resource.trigger(
+            "PUT",
             "/_matrix/federation/v1/send/1000000/",
-            _make_edu_json("farm", "m.typing",
+            _make_edu_json(
+                "farm",
+                "m.typing",
                 content={
                     "room_id": self.room_id,
                     "user_id": self.u_onion.to_string(),
@@ -257,16 +264,13 @@ class TypingNotificationsTestCase(unittest.TestCase):
             room_ids=[self.room_id],
             from_key=0
         )
-        self.assertEquals(
-            events[0],
-            [
-                {"type": "m.typing",
-                 "room_id": self.room_id,
-                 "content": {
-                     "user_ids": [self.u_onion.to_string()],
-                }},
-            ]
-        )
+        self.assertEquals(events[0], [{
+            "type": "m.typing",
+            "room_id": self.room_id,
+            "content": {
+                "user_ids": [self.u_onion.to_string()],
+            },
+        }])
 
     @defer.inlineCallbacks
     def test_stopped_typing(self):
@@ -274,9 +278,12 @@ class TypingNotificationsTestCase(unittest.TestCase):
 
         put_json = self.mock_http_client.put_json
         put_json.expect_call_and_return(
-            call("farm",
+            call(
+                "farm",
                 path="/_matrix/federation/v1/send/1000000/",
-                data=_expect_edu("farm", "m.typing",
+                data=_expect_edu(
+                    "farm",
+                    "m.typing",
                     content={
                         "room_id": self.room_id,
                         "user_id": self.u_apple.to_string(),
@@ -317,16 +324,13 @@ class TypingNotificationsTestCase(unittest.TestCase):
             room_ids=[self.room_id],
             from_key=0,
         )
-        self.assertEquals(
-            events[0],
-            [
-                {"type": "m.typing",
-                 "room_id": self.room_id,
-                 "content": {
-                     "user_ids": [],
-                }},
-            ]
-        )
+        self.assertEquals(events[0], [{
+            "type": "m.typing",
+            "room_id": self.room_id,
+            "content": {
+                "user_ids": [],
+            },
+        }])
 
     @defer.inlineCallbacks
     def test_typing_timeout(self):
@@ -351,16 +355,13 @@ class TypingNotificationsTestCase(unittest.TestCase):
             room_ids=[self.room_id],
             from_key=0,
         )
-        self.assertEquals(
-            events[0],
-            [
-                {"type": "m.typing",
-                 "room_id": self.room_id,
-                 "content": {
-                     "user_ids": [self.u_apple.to_string()],
-                }},
-            ]
-        )
+        self.assertEquals(events[0], [{
+            "type": "m.typing",
+            "room_id": self.room_id,
+            "content": {
+                "user_ids": [self.u_apple.to_string()],
+            },
+        }])
 
         self.clock.advance_time(11)
 
@@ -373,16 +374,13 @@ class TypingNotificationsTestCase(unittest.TestCase):
             room_ids=[self.room_id],
             from_key=1,
         )
-        self.assertEquals(
-            events[0],
-            [
-                {"type": "m.typing",
-                 "room_id": self.room_id,
-                 "content": {
-                     "user_ids": [],
-                }},
-            ]
-        )
+        self.assertEquals(events[0], [{
+            "type": "m.typing",
+            "room_id": self.room_id,
+            "content": {
+                "user_ids": [],
+            },
+        }])
 
         # SYN-230 - see if we can still set after timeout
 
@@ -403,13 +401,10 @@ class TypingNotificationsTestCase(unittest.TestCase):
             room_ids=[self.room_id],
             from_key=0,
         )
-        self.assertEquals(
-            events[0],
-            [
-                {"type": "m.typing",
-                 "room_id": self.room_id,
-                 "content": {
-                     "user_ids": [self.u_apple.to_string()],
-                }},
-            ]
-        )
+        self.assertEquals(events[0], [{
+            "type": "m.typing",
+            "room_id": self.room_id,
+            "content": {
+                "user_ids": [self.u_apple.to_string()],
+            },
+        }])
diff --git a/tests/metrics/test_metric.py b/tests/metrics/test_metric.py
index f9e5e5af01..f3c1927ce1 100644
--- a/tests/metrics/test_metric.py
+++ b/tests/metrics/test_metric.py
@@ -61,6 +61,9 @@ class CounterMetricTestCase(unittest.TestCase):
             'vector{method="PUT"} 1',
         ])
 
+        # Check that passing too few values errors
+        self.assertRaises(ValueError, counter.inc)
+
 
 class CallbackMetricTestCase(unittest.TestCase):
 
diff --git a/tests/replication/__init__.py b/tests/replication/__init__.py
new file mode 100644
index 0000000000..b7df13c9ee
--- /dev/null
+++ b/tests/replication/__init__.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+# Copyright 2016 OpenMarket Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/tests/replication/test_resource.py b/tests/replication/test_resource.py
new file mode 100644
index 0000000000..38daaf87e2
--- /dev/null
+++ b/tests/replication/test_resource.py
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+# Copyright 2016 OpenMarket Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from synapse.replication.resource import ReplicationResource
+from synapse.types import Requester, UserID
+
+from twisted.internet import defer
+from tests import unittest
+from tests.utils import setup_test_homeserver
+from mock import Mock, NonCallableMock
+import json
+import contextlib
+
+
+class ReplicationResourceCase(unittest.TestCase):
+    @defer.inlineCallbacks
+    def setUp(self):
+        self.hs = yield setup_test_homeserver(
+            "red",
+            http_client=None,
+            replication_layer=Mock(),
+            ratelimiter=NonCallableMock(spec_set=[
+                "send_message",
+            ]),
+        )
+        self.user = UserID.from_string("@seeing:red")
+
+        self.hs.get_ratelimiter().send_message.return_value = (True, 0)
+
+        self.resource = ReplicationResource(self.hs)
+
+    @defer.inlineCallbacks
+    def test_streams(self):
+        # Passing "-1" returns the current stream positions
+        code, body = yield self.get(streams="-1")
+        self.assertEquals(code, 200)
+        self.assertEquals(body["streams"]["field_names"], ["name", "position"])
+        position = body["streams"]["position"]
+        # Passing the current position returns an empty response after the
+        # timeout
+        get = self.get(streams=str(position), timeout="0")
+        self.hs.clock.advance_time_msec(1)
+        code, body = yield get
+        self.assertEquals(code, 200)
+        self.assertEquals(body, {})
+
+    @defer.inlineCallbacks
+    def test_events(self):
+        get = self.get(events="-1", timeout="0")
+        yield self.hs.get_handlers().room_creation_handler.create_room(
+            Requester(self.user, "", False), {}
+        )
+        code, body = yield get
+        self.assertEquals(code, 200)
+        self.assertEquals(body["events"]["field_names"], [
+            "position", "internal", "json"
+        ])
+
+    @defer.inlineCallbacks
+    def test_presence(self):
+        get = self.get(presence="-1")
+        yield self.hs.get_handlers().presence_handler.set_state(
+            self.user, {"presence": "online"}
+        )
+        code, body = yield get
+        self.assertEquals(code, 200)
+        self.assertEquals(body["presence"]["field_names"], [
+            "position", "user_id", "state", "last_active_ts",
+            "last_federation_update_ts", "last_user_sync_ts",
+            "status_msg", "currently_active",
+        ])
+
+    @defer.inlineCallbacks
+    def test_typing(self):
+        room_id = yield self.create_room()
+        get = self.get(typing="-1")
+        yield self.hs.get_handlers().typing_notification_handler.started_typing(
+            self.user, self.user, room_id, timeout=2
+        )
+        code, body = yield get
+        self.assertEquals(code, 200)
+        self.assertEquals(body["typing"]["field_names"], [
+            "position", "room_id", "typing"
+        ])
+
+    @defer.inlineCallbacks
+    def test_receipts(self):
+        room_id = yield self.create_room()
+        event_id = yield self.send_text_message(room_id, "Hello, World")
+        get = self.get(receipts="-1")
+        yield self.hs.get_handlers().receipts_handler.received_client_receipt(
+            room_id, "m.read", self.user.to_string(), event_id
+        )
+        code, body = yield get
+        self.assertEquals(code, 200)
+        self.assertEquals(body["receipts"]["field_names"], [
+            "position", "room_id", "receipt_type", "user_id", "event_id", "data"
+        ])
+
+    def _test_timeout(stream):
+        """Check that a request for the given stream timesout"""
+        @defer.inlineCallbacks
+        def test_timeout(self):
+            get = self.get(**{stream: "-1", "timeout": "0"})
+            self.hs.clock.advance_time_msec(1)
+            code, body = yield get
+            self.assertEquals(code, 200)
+            self.assertEquals(body, {})
+        test_timeout.__name__ = "test_timeout_%s" % (stream)
+        return test_timeout
+
+    test_timeout_events = _test_timeout("events")
+    test_timeout_presence = _test_timeout("presence")
+    test_timeout_typing = _test_timeout("typing")
+    test_timeout_receipts = _test_timeout("receipts")
+    test_timeout_user_account_data = _test_timeout("user_account_data")
+    test_timeout_room_account_data = _test_timeout("room_account_data")
+    test_timeout_tag_account_data = _test_timeout("tag_account_data")
+    test_timeout_backfill = _test_timeout("backfill")
+
+    @defer.inlineCallbacks
+    def send_text_message(self, room_id, message):
+        handler = self.hs.get_handlers().message_handler
+        event = yield handler.create_and_send_nonmember_event({
+            "type": "m.room.message",
+            "content": {"body": "message", "msgtype": "m.text"},
+            "room_id": room_id,
+            "sender": self.user.to_string(),
+        })
+        defer.returnValue(event.event_id)
+
+    @defer.inlineCallbacks
+    def create_room(self):
+        result = yield self.hs.get_handlers().room_creation_handler.create_room(
+            Requester(self.user, "", False), {}
+        )
+        defer.returnValue(result["room_id"])
+
+    @defer.inlineCallbacks
+    def get(self, **params):
+        request = NonCallableMock(spec_set=[
+            "write", "finish", "setResponseCode", "setHeader", "args",
+            "method", "processing"
+        ])
+
+        request.method = "GET"
+        request.args = {k: [v] for k, v in params.items()}
+
+        @contextlib.contextmanager
+        def processing():
+            yield
+        request.processing = processing
+
+        yield self.resource._async_render_GET(request)
+        self.assertTrue(request.finish.called)
+
+        if request.setResponseCode.called:
+            response_code = request.setResponseCode.call_args[0][0]
+        else:
+            response_code = 200
+
+        response_json = "".join(
+            call[0][0] for call in request.write.call_args_list
+        )
+        response_body = json.loads(response_json)
+
+        defer.returnValue((response_code, response_body))
diff --git a/tests/rest/client/v1/__init__.py b/tests/rest/client/v1/__init__.py
index d0e9399dda..bfebb0f644 100644
--- a/tests/rest/client/v1/__init__.py
+++ b/tests/rest/client/v1/__init__.py
@@ -12,4 +12,3 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
diff --git a/tests/rest/client/v1/test_presence.py b/tests/rest/client/v1/test_presence.py
deleted file mode 100644
index 8d7cfd79ab..0000000000
--- a/tests/rest/client/v1/test_presence.py
+++ /dev/null
@@ -1,412 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Tests REST events for /presence paths."""
-from tests import unittest
-from twisted.internet import defer
-
-from mock import Mock
-
-from ....utils import MockHttpResource, setup_test_homeserver
-
-from synapse.api.constants import PresenceState
-from synapse.handlers.presence import PresenceHandler
-from synapse.rest.client.v1 import presence
-from synapse.rest.client.v1 import events
-from synapse.types import Requester, UserID
-from synapse.util.async import run_on_reactor
-
-from collections import namedtuple
-
-
-OFFLINE = PresenceState.OFFLINE
-UNAVAILABLE = PresenceState.UNAVAILABLE
-ONLINE = PresenceState.ONLINE
-
-
-myid = "@apple:test"
-PATH_PREFIX = "/_matrix/client/api/v1"
-
-
-class NullSource(object):
-    """This event source never yields any events and its token remains at
-    zero. It may be useful for unit-testing."""
-    def __init__(self, hs):
-        pass
-
-    def get_new_events(
-            self,
-            user,
-            from_key,
-            room_ids=None,
-            limit=None,
-            is_guest=None
-    ):
-        return defer.succeed(([], from_key))
-
-    def get_current_key(self, direction='f'):
-        return defer.succeed(0)
-
-    def get_pagination_rows(self, user, pagination_config, key):
-        return defer.succeed(([], pagination_config.from_key))
-
-
-class JustPresenceHandlers(object):
-    def __init__(self, hs):
-        self.presence_handler = PresenceHandler(hs)
-
-
-class PresenceStateTestCase(unittest.TestCase):
-
-    @defer.inlineCallbacks
-    def setUp(self):
-        self.mock_resource = MockHttpResource(prefix=PATH_PREFIX)
-        hs = yield setup_test_homeserver(
-            datastore=Mock(spec=[
-                "get_presence_state",
-                "set_presence_state",
-                "insert_client_ip",
-            ]),
-            http_client=None,
-            resource_for_client=self.mock_resource,
-            resource_for_federation=self.mock_resource,
-        )
-        hs.handlers = JustPresenceHandlers(hs)
-
-        self.datastore = hs.get_datastore()
-        self.datastore.get_app_service_by_token = Mock(return_value=None)
-
-        def get_presence_list(*a, **kw):
-            return defer.succeed([])
-        self.datastore.get_presence_list = get_presence_list
-
-        def _get_user_by_access_token(token=None, allow_guest=False):
-            return {
-                "user": UserID.from_string(myid),
-                "token_id": 1,
-                "is_guest": False,
-            }
-
-        hs.get_v1auth()._get_user_by_access_token = _get_user_by_access_token
-
-        room_member_handler = hs.handlers.room_member_handler = Mock(
-            spec=[
-                "get_joined_rooms_for_user",
-            ]
-        )
-
-        def get_rooms_for_user(user):
-            return defer.succeed([])
-        room_member_handler.get_joined_rooms_for_user = get_rooms_for_user
-
-        presence.register_servlets(hs, self.mock_resource)
-
-        self.u_apple = UserID.from_string(myid)
-
-    @defer.inlineCallbacks
-    def test_get_my_status(self):
-        mocked_get = self.datastore.get_presence_state
-        mocked_get.return_value = defer.succeed(
-            {"state": ONLINE, "status_msg": "Available"}
-        )
-
-        (code, response) = yield self.mock_resource.trigger("GET",
-                "/presence/%s/status" % (myid), None)
-
-        self.assertEquals(200, code)
-        self.assertEquals(
-            {"presence": ONLINE, "status_msg": "Available"},
-            response
-        )
-        mocked_get.assert_called_with("apple")
-
-    @defer.inlineCallbacks
-    def test_set_my_status(self):
-        mocked_set = self.datastore.set_presence_state
-        mocked_set.return_value = defer.succeed({"state": OFFLINE})
-
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                "/presence/%s/status" % (myid),
-                '{"presence": "unavailable", "status_msg": "Away"}')
-
-        self.assertEquals(200, code)
-        mocked_set.assert_called_with("apple",
-            {"state": UNAVAILABLE, "status_msg": "Away"}
-        )
-
-
-class PresenceListTestCase(unittest.TestCase):
-
-    @defer.inlineCallbacks
-    def setUp(self):
-        self.mock_resource = MockHttpResource(prefix=PATH_PREFIX)
-
-        hs = yield setup_test_homeserver(
-            datastore=Mock(spec=[
-                "has_presence_state",
-                "get_presence_state",
-                "allow_presence_visible",
-                "is_presence_visible",
-                "add_presence_list_pending",
-                "set_presence_list_accepted",
-                "del_presence_list",
-                "get_presence_list",
-                "insert_client_ip",
-            ]),
-            http_client=None,
-            resource_for_client=self.mock_resource,
-            resource_for_federation=self.mock_resource,
-        )
-        hs.handlers = JustPresenceHandlers(hs)
-
-        self.datastore = hs.get_datastore()
-        self.datastore.get_app_service_by_token = Mock(return_value=None)
-
-        def has_presence_state(user_localpart):
-            return defer.succeed(
-                user_localpart in ("apple", "banana",)
-            )
-        self.datastore.has_presence_state = has_presence_state
-
-        def _get_user_by_access_token(token=None, allow_guest=False):
-            return {
-                "user": UserID.from_string(myid),
-                "token_id": 1,
-                "is_guest": False,
-            }
-
-        hs.handlers.room_member_handler = Mock(
-            spec=[
-                "get_joined_rooms_for_user",
-            ]
-        )
-
-        hs.get_v1auth()._get_user_by_access_token = _get_user_by_access_token
-
-        presence.register_servlets(hs, self.mock_resource)
-
-        self.u_apple = UserID.from_string("@apple:test")
-        self.u_banana = UserID.from_string("@banana:test")
-
-    @defer.inlineCallbacks
-    def test_get_my_list(self):
-        self.datastore.get_presence_list.return_value = defer.succeed(
-            [{"observed_user_id": "@banana:test", "accepted": True}],
-        )
-
-        (code, response) = yield self.mock_resource.trigger("GET",
-                "/presence/list/%s" % (myid), None)
-
-        self.assertEquals(200, code)
-        self.assertEquals([
-            {"user_id": "@banana:test", "presence": OFFLINE, "accepted": True},
-        ], response)
-
-        self.datastore.get_presence_list.assert_called_with(
-            "apple", accepted=True
-        )
-
-    @defer.inlineCallbacks
-    def test_invite(self):
-        self.datastore.add_presence_list_pending.return_value = (
-            defer.succeed(())
-        )
-        self.datastore.is_presence_visible.return_value = defer.succeed(
-            True
-        )
-
-        (code, response) = yield self.mock_resource.trigger("POST",
-            "/presence/list/%s" % (myid),
-            """{"invite": ["@banana:test"]}"""
-        )
-
-        self.assertEquals(200, code)
-
-        self.datastore.add_presence_list_pending.assert_called_with(
-            "apple", "@banana:test"
-        )
-        self.datastore.set_presence_list_accepted.assert_called_with(
-            "apple", "@banana:test"
-        )
-
-    @defer.inlineCallbacks
-    def test_drop(self):
-        self.datastore.del_presence_list.return_value = (
-            defer.succeed(())
-        )
-
-        (code, response) = yield self.mock_resource.trigger("POST",
-            "/presence/list/%s" % (myid),
-            """{"drop": ["@banana:test"]}"""
-        )
-
-        self.assertEquals(200, code)
-
-        self.datastore.del_presence_list.assert_called_with(
-            "apple", "@banana:test"
-        )
-
-
-class PresenceEventStreamTestCase(unittest.TestCase):
-    @defer.inlineCallbacks
-    def setUp(self):
-        self.mock_resource = MockHttpResource(prefix=PATH_PREFIX)
-
-        # HIDEOUS HACKERY
-        # TODO(paul): This should be injected in via the HomeServer DI system
-        from synapse.streams.events import (
-            PresenceEventSource, EventSources
-        )
-
-        old_SOURCE_TYPES = EventSources.SOURCE_TYPES
-        def tearDown():
-            EventSources.SOURCE_TYPES = old_SOURCE_TYPES
-        self.tearDown = tearDown
-
-        EventSources.SOURCE_TYPES = {
-            k: NullSource for k in old_SOURCE_TYPES.keys()
-        }
-        EventSources.SOURCE_TYPES["presence"] = PresenceEventSource
-
-        clock = Mock(spec=[
-            "call_later",
-            "cancel_call_later",
-            "time_msec",
-            "looping_call",
-        ])
-
-        clock.time_msec.return_value = 1000000
-
-        hs = yield setup_test_homeserver(
-            http_client=None,
-            resource_for_client=self.mock_resource,
-            resource_for_federation=self.mock_resource,
-            datastore=Mock(spec=[
-                "set_presence_state",
-                "get_presence_list",
-                "get_rooms_for_user",
-            ]),
-            clock=clock,
-        )
-
-        def _get_user_by_req(req=None, allow_guest=False):
-            return Requester(UserID.from_string(myid), "", False)
-
-        hs.get_v1auth().get_user_by_req = _get_user_by_req
-
-        presence.register_servlets(hs, self.mock_resource)
-        events.register_servlets(hs, self.mock_resource)
-
-        hs.handlers.room_member_handler = Mock(spec=[])
-
-        self.room_members = []
-
-        def get_rooms_for_user(user):
-            if user in self.room_members:
-                return ["a-room"]
-            else:
-                return []
-        hs.handlers.room_member_handler.get_joined_rooms_for_user = get_rooms_for_user
-        hs.handlers.room_member_handler.get_room_members = (
-            lambda r: self.room_members if r == "a-room" else []
-        )
-        hs.handlers.room_member_handler._filter_events_for_client = (
-            lambda user_id, events, **kwargs: events
-        )
-
-        self.mock_datastore = hs.get_datastore()
-        self.mock_datastore.get_app_service_by_token = Mock(return_value=None)
-        self.mock_datastore.get_app_service_by_user_id = Mock(
-            return_value=defer.succeed(None)
-        )
-        self.mock_datastore.get_rooms_for_user = (
-            lambda u: [
-                namedtuple("Room", "room_id")(r)
-                for r in get_rooms_for_user(UserID.from_string(u))
-            ]
-        )
-
-        def get_profile_displayname(user_id):
-            return defer.succeed("Frank")
-        self.mock_datastore.get_profile_displayname = get_profile_displayname
-
-        def get_profile_avatar_url(user_id):
-            return defer.succeed(None)
-        self.mock_datastore.get_profile_avatar_url = get_profile_avatar_url
-
-        def user_rooms_intersect(user_list):
-            room_member_ids = map(lambda u: u.to_string(), self.room_members)
-
-            shared = all(map(lambda i: i in room_member_ids, user_list))
-            return defer.succeed(shared)
-        self.mock_datastore.user_rooms_intersect = user_rooms_intersect
-
-        def get_joined_hosts_for_room(room_id):
-            return []
-        self.mock_datastore.get_joined_hosts_for_room = get_joined_hosts_for_room
-
-        self.presence = hs.get_handlers().presence_handler
-
-        self.u_apple = UserID.from_string("@apple:test")
-        self.u_banana = UserID.from_string("@banana:test")
-
-    @defer.inlineCallbacks
-    def test_shortpoll(self):
-        self.room_members = [self.u_apple, self.u_banana]
-
-        self.mock_datastore.set_presence_state.return_value = defer.succeed(
-            {"state": ONLINE}
-        )
-        self.mock_datastore.get_presence_list.return_value = defer.succeed(
-            []
-        )
-
-        (code, response) = yield self.mock_resource.trigger("GET",
-                "/events?timeout=0", None)
-
-        self.assertEquals(200, code)
-
-        # We've forced there to be only one data stream so the tokens will
-        # all be ours
-
-        # I'll already get my own presence state change
-        self.assertEquals({"start": "0_1_0_0_0", "end": "0_1_0_0_0", "chunk": []},
-            response
-        )
-
-        self.mock_datastore.set_presence_state.return_value = defer.succeed(
-            {"state": ONLINE}
-        )
-        self.mock_datastore.get_presence_list.return_value = defer.succeed([])
-
-        yield self.presence.set_state(self.u_banana, self.u_banana,
-            state={"presence": ONLINE}
-        )
-
-        yield run_on_reactor()
-
-        (code, response) = yield self.mock_resource.trigger("GET",
-                "/events?from=s0_1_0&timeout=0", None)
-
-        self.assertEquals(200, code)
-        self.assertEquals({"start": "s0_1_0_0_0", "end": "s0_2_0_0_0", "chunk": [
-            {"type": "m.presence",
-             "content": {
-                 "user_id": "@banana:test",
-                 "presence": ONLINE,
-                 "displayname": "Frank",
-                 "last_active_ago": 0,
-            }},
-        ]}, response)
diff --git a/tests/rest/client/v1/test_profile.py b/tests/rest/client/v1/test_profile.py
index c1a3f52043..0785965de2 100644
--- a/tests/rest/client/v1/test_profile.py
+++ b/tests/rest/client/v1/test_profile.py
@@ -65,8 +65,9 @@ class ProfileTestCase(unittest.TestCase):
         mocked_get = self.mock_handler.get_displayname
         mocked_get.return_value = defer.succeed("Frank")
 
-        (code, response) = yield self.mock_resource.trigger("GET",
-                "/profile/%s/displayname" % (myid), None)
+        (code, response) = yield self.mock_resource.trigger(
+            "GET", "/profile/%s/displayname" % (myid), None
+        )
 
         self.assertEquals(200, code)
         self.assertEquals({"displayname": "Frank"}, response)
@@ -77,9 +78,11 @@ class ProfileTestCase(unittest.TestCase):
         mocked_set = self.mock_handler.set_displayname
         mocked_set.return_value = defer.succeed(())
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                "/profile/%s/displayname" % (myid),
-                '{"displayname": "Frank Jr."}')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT",
+            "/profile/%s/displayname" % (myid),
+            '{"displayname": "Frank Jr."}'
+        )
 
         self.assertEquals(200, code)
         self.assertEquals(mocked_set.call_args[0][0].localpart, "1234ABCD")
@@ -91,19 +94,23 @@ class ProfileTestCase(unittest.TestCase):
         mocked_set = self.mock_handler.set_displayname
         mocked_set.side_effect = AuthError(400, "message")
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                "/profile/%s/displayname" % ("@4567:test"), '"Frank Jr."')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", "/profile/%s/displayname" % ("@4567:test"), '"Frank Jr."'
+        )
 
-        self.assertTrue(400 <= code < 499,
-                msg="code %d is in the 4xx range" % (code))
+        self.assertTrue(
+            400 <= code < 499,
+            msg="code %d is in the 4xx range" % (code)
+        )
 
     @defer.inlineCallbacks
     def test_get_other_name(self):
         mocked_get = self.mock_handler.get_displayname
         mocked_get.return_value = defer.succeed("Bob")
 
-        (code, response) = yield self.mock_resource.trigger("GET",
-                "/profile/%s/displayname" % ("@opaque:elsewhere"), None)
+        (code, response) = yield self.mock_resource.trigger(
+            "GET", "/profile/%s/displayname" % ("@opaque:elsewhere"), None
+        )
 
         self.assertEquals(200, code)
         self.assertEquals({"displayname": "Bob"}, response)
@@ -113,19 +120,23 @@ class ProfileTestCase(unittest.TestCase):
         mocked_set = self.mock_handler.set_displayname
         mocked_set.side_effect = SynapseError(400, "message")
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                "/profile/%s/displayname" % ("@opaque:elsewhere"), None)
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", "/profile/%s/displayname" % ("@opaque:elsewhere"), None
+        )
 
-        self.assertTrue(400 <= code <= 499,
-                msg="code %d is in the 4xx range" % (code))
+        self.assertTrue(
+            400 <= code <= 499,
+            msg="code %d is in the 4xx range" % (code)
+        )
 
     @defer.inlineCallbacks
     def test_get_my_avatar(self):
         mocked_get = self.mock_handler.get_avatar_url
         mocked_get.return_value = defer.succeed("http://my.server/me.png")
 
-        (code, response) = yield self.mock_resource.trigger("GET",
-                "/profile/%s/avatar_url" % (myid), None)
+        (code, response) = yield self.mock_resource.trigger(
+            "GET", "/profile/%s/avatar_url" % (myid), None
+        )
 
         self.assertEquals(200, code)
         self.assertEquals({"avatar_url": "http://my.server/me.png"}, response)
@@ -136,12 +147,13 @@ class ProfileTestCase(unittest.TestCase):
         mocked_set = self.mock_handler.set_avatar_url
         mocked_set.return_value = defer.succeed(())
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                "/profile/%s/avatar_url" % (myid),
-                '{"avatar_url": "http://my.server/pic.gif"}')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT",
+            "/profile/%s/avatar_url" % (myid),
+            '{"avatar_url": "http://my.server/pic.gif"}'
+        )
 
         self.assertEquals(200, code)
         self.assertEquals(mocked_set.call_args[0][0].localpart, "1234ABCD")
         self.assertEquals(mocked_set.call_args[0][1].localpart, "1234ABCD")
-        self.assertEquals(mocked_set.call_args[0][2],
-                "http://my.server/pic.gif")
+        self.assertEquals(mocked_set.call_args[0][2], "http://my.server/pic.gif")
diff --git a/tests/rest/client/v1/test_rooms.py b/tests/rest/client/v1/test_rooms.py
index ad5dd3bd6e..afca5303ba 100644
--- a/tests/rest/client/v1/test_rooms.py
+++ b/tests/rest/client/v1/test_rooms.py
@@ -82,19 +82,22 @@ class RoomPermissionsTestCase(RestTestCase):
                                                              is_public=True)
 
         # send a message in one of the rooms
-        self.created_rmid_msg_path = ("/rooms/%s/send/m.room.message/a1" %
-                                (self.created_rmid))
+        self.created_rmid_msg_path = (
+            "/rooms/%s/send/m.room.message/a1" % (self.created_rmid)
+        )
         (code, response) = yield self.mock_resource.trigger(
-                           "PUT",
-                           self.created_rmid_msg_path,
-                           '{"msgtype":"m.text","body":"test msg"}')
+            "PUT",
+            self.created_rmid_msg_path,
+            '{"msgtype":"m.text","body":"test msg"}'
+        )
         self.assertEquals(200, code, msg=str(response))
 
         # set topic for public room
         (code, response) = yield self.mock_resource.trigger(
-                           "PUT",
-                           "/rooms/%s/state/m.room.topic" % self.created_public_rmid,
-                           '{"topic":"Public Room Topic"}')
+            "PUT",
+            "/rooms/%s/state/m.room.topic" % self.created_public_rmid,
+            '{"topic":"Public Room Topic"}'
+        )
         self.assertEquals(200, code, msg=str(response))
 
         # auth as user_id now
@@ -103,37 +106,6 @@ class RoomPermissionsTestCase(RestTestCase):
     def tearDown(self):
         pass
 
-#    @defer.inlineCallbacks
-#    def test_get_message(self):
-#        # get message in uncreated room, expect 403
-#        (code, response) = yield self.mock_resource.trigger_get(
-#                           "/rooms/noroom/messages/someid/m1")
-#        self.assertEquals(403, code, msg=str(response))
-#
-#        # get message in created room not joined (no state), expect 403
-#        (code, response) = yield self.mock_resource.trigger_get(
-#                           self.created_rmid_msg_path)
-#        self.assertEquals(403, code, msg=str(response))
-#
-#        # get message in created room and invited, expect 403
-#        yield self.invite(room=self.created_rmid, src=self.rmcreator_id,
-#                          targ=self.user_id)
-#        (code, response) = yield self.mock_resource.trigger_get(
-#                           self.created_rmid_msg_path)
-#        self.assertEquals(403, code, msg=str(response))
-#
-#        # get message in created room and joined, expect 200
-#        yield self.join(room=self.created_rmid, user=self.user_id)
-#        (code, response) = yield self.mock_resource.trigger_get(
-#                           self.created_rmid_msg_path)
-#        self.assertEquals(200, code, msg=str(response))
-#
-#        # get message in created room and left, expect 403
-#        yield self.leave(room=self.created_rmid, user=self.user_id)
-#        (code, response) = yield self.mock_resource.trigger_get(
-#                           self.created_rmid_msg_path)
-#        self.assertEquals(403, code, msg=str(response))
-
     @defer.inlineCallbacks
     def test_send_message(self):
         msg_content = '{"msgtype":"m.text","body":"hello"}'
@@ -195,25 +167,30 @@ class RoomPermissionsTestCase(RestTestCase):
 
         # set/get topic in uncreated room, expect 403
         (code, response) = yield self.mock_resource.trigger(
-                           "PUT", "/rooms/%s/state/m.room.topic" % self.uncreated_rmid,
-                           topic_content)
+            "PUT", "/rooms/%s/state/m.room.topic" % self.uncreated_rmid,
+            topic_content
+        )
         self.assertEquals(403, code, msg=str(response))
         (code, response) = yield self.mock_resource.trigger_get(
-                           "/rooms/%s/state/m.room.topic" % self.uncreated_rmid)
+            "/rooms/%s/state/m.room.topic" % self.uncreated_rmid
+        )
         self.assertEquals(403, code, msg=str(response))
 
         # set/get topic in created PRIVATE room not joined, expect 403
         (code, response) = yield self.mock_resource.trigger(
-                           "PUT", topic_path, topic_content)
+            "PUT", topic_path, topic_content
+        )
         self.assertEquals(403, code, msg=str(response))
         (code, response) = yield self.mock_resource.trigger_get(topic_path)
         self.assertEquals(403, code, msg=str(response))
 
         # set topic in created PRIVATE room and invited, expect 403
-        yield self.invite(room=self.created_rmid, src=self.rmcreator_id,
-                          targ=self.user_id)
+        yield self.invite(
+            room=self.created_rmid, src=self.rmcreator_id, targ=self.user_id
+        )
         (code, response) = yield self.mock_resource.trigger(
-                           "PUT", topic_path, topic_content)
+            "PUT", topic_path, topic_content
+        )
         self.assertEquals(403, code, msg=str(response))
 
         # get topic in created PRIVATE room and invited, expect 403
@@ -226,7 +203,8 @@ class RoomPermissionsTestCase(RestTestCase):
         # Only room ops can set topic by default
         self.auth_user_id = self.rmcreator_id
         (code, response) = yield self.mock_resource.trigger(
-                           "PUT", topic_path, topic_content)
+            "PUT", topic_path, topic_content
+        )
         self.assertEquals(200, code, msg=str(response))
         self.auth_user_id = self.user_id
 
@@ -237,30 +215,31 @@ class RoomPermissionsTestCase(RestTestCase):
         # set/get topic in created PRIVATE room and left, expect 403
         yield self.leave(room=self.created_rmid, user=self.user_id)
         (code, response) = yield self.mock_resource.trigger(
-                           "PUT", topic_path, topic_content)
+            "PUT", topic_path, topic_content
+        )
         self.assertEquals(403, code, msg=str(response))
         (code, response) = yield self.mock_resource.trigger_get(topic_path)
         self.assertEquals(200, code, msg=str(response))
 
         # get topic in PUBLIC room, not joined, expect 403
         (code, response) = yield self.mock_resource.trigger_get(
-                           "/rooms/%s/state/m.room.topic" % self.created_public_rmid)
+            "/rooms/%s/state/m.room.topic" % self.created_public_rmid
+        )
         self.assertEquals(403, code, msg=str(response))
 
         # set topic in PUBLIC room, not joined, expect 403
         (code, response) = yield self.mock_resource.trigger(
-                           "PUT",
-                           "/rooms/%s/state/m.room.topic" % self.created_public_rmid,
-                           topic_content)
+            "PUT",
+            "/rooms/%s/state/m.room.topic" % self.created_public_rmid,
+            topic_content
+        )
         self.assertEquals(403, code, msg=str(response))
 
     @defer.inlineCallbacks
     def _test_get_membership(self, room=None, members=[], expect_code=None):
-        path = "/rooms/%s/state/m.room.member/%s"
         for member in members:
-            (code, response) = yield self.mock_resource.trigger_get(
-                               path %
-                               (room, member))
+            path = "/rooms/%s/state/m.room.member/%s" % (room, member)
+            (code, response) = yield self.mock_resource.trigger_get(path)
             self.assertEquals(expect_code, code)
 
     @defer.inlineCallbacks
@@ -461,20 +440,23 @@ class RoomsMemberListTestCase(RestTestCase):
     def test_get_member_list(self):
         room_id = yield self.create_room_as(self.user_id)
         (code, response) = yield self.mock_resource.trigger_get(
-                           "/rooms/%s/members" % room_id)
+            "/rooms/%s/members" % room_id
+        )
         self.assertEquals(200, code, msg=str(response))
 
     @defer.inlineCallbacks
     def test_get_member_list_no_room(self):
         (code, response) = yield self.mock_resource.trigger_get(
-                           "/rooms/roomdoesnotexist/members")
+            "/rooms/roomdoesnotexist/members"
+        )
         self.assertEquals(403, code, msg=str(response))
 
     @defer.inlineCallbacks
     def test_get_member_list_no_permission(self):
         room_id = yield self.create_room_as("@some_other_guy:red")
         (code, response) = yield self.mock_resource.trigger_get(
-                           "/rooms/%s/members" % room_id)
+            "/rooms/%s/members" % room_id
+        )
         self.assertEquals(403, code, msg=str(response))
 
     @defer.inlineCallbacks
@@ -636,34 +618,41 @@ class RoomTopicTestCase(RestTestCase):
     @defer.inlineCallbacks
     def test_invalid_puts(self):
         # missing keys or invalid json
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           self.path, '{}')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", self.path, '{}'
+        )
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           self.path, '{"_name":"bob"}')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", self.path, '{"_name":"bob"}'
+        )
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           self.path, '{"nao')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", self.path, '{"nao'
+        )
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           self.path, '[{"_name":"bob"},{"_name":"jill"}]')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", self.path, '[{"_name":"bob"},{"_name":"jill"}]'
+        )
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           self.path, 'text only')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", self.path, 'text only'
+        )
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           self.path, '')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", self.path, ''
+        )
         self.assertEquals(400, code, msg=str(response))
 
         # valid key, wrong type
         content = '{"topic":["Topic name"]}'
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           self.path, content)
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", self.path, content
+        )
         self.assertEquals(400, code, msg=str(response))
 
     @defer.inlineCallbacks
@@ -674,8 +663,9 @@ class RoomTopicTestCase(RestTestCase):
 
         # valid put
         content = '{"topic":"Topic name"}'
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           self.path, content)
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", self.path, content
+        )
         self.assertEquals(200, code, msg=str(response))
 
         # valid get
@@ -687,8 +677,9 @@ class RoomTopicTestCase(RestTestCase):
     def test_rooms_topic_with_extra_keys(self):
         # valid put with extra keys
         content = '{"topic":"Seasons","subtopic":"Summer"}'
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           self.path, content)
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", self.path, content
+        )
         self.assertEquals(200, code, msg=str(response))
 
         # valid get
@@ -740,33 +731,38 @@ class RoomMemberStateTestCase(RestTestCase):
     def test_invalid_puts(self):
         path = "/rooms/%s/state/m.room.member/%s" % (self.room_id, self.user_id)
         # missing keys or invalid json
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           path, '{}')
+        (code, response) = yield self.mock_resource.trigger("PUT", path, '{}')
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           path, '{"_name":"bob"}')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", path, '{"_name":"bob"}'
+        )
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           path, '{"nao')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", path, '{"nao'
+        )
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           path, '[{"_name":"bob"},{"_name":"jill"}]')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", path, '[{"_name":"bob"},{"_name":"jill"}]'
+        )
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           path, 'text only')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", path, 'text only'
+        )
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           path, '')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", path, ''
+        )
         self.assertEquals(400, code, msg=str(response))
 
         # valid keys, wrong types
-        content = ('{"membership":["%s","%s","%s"]}' %
-                  (Membership.INVITE, Membership.JOIN, Membership.LEAVE))
+        content = ('{"membership":["%s","%s","%s"]}' % (
+            Membership.INVITE, Membership.JOIN, Membership.LEAVE
+        ))
         (code, response) = yield self.mock_resource.trigger("PUT", path, content)
         self.assertEquals(400, code, msg=str(response))
 
@@ -813,8 +809,9 @@ class RoomMemberStateTestCase(RestTestCase):
         )
 
         # valid invite message with custom key
-        content = ('{"membership":"%s","invite_text":"%s"}' %
-                    (Membership.INVITE, "Join us!"))
+        content = ('{"membership":"%s","invite_text":"%s"}' % (
+            Membership.INVITE, "Join us!"
+        ))
         (code, response) = yield self.mock_resource.trigger("PUT", path, content)
         self.assertEquals(200, code, msg=str(response))
 
@@ -867,28 +864,34 @@ class RoomMessagesTestCase(RestTestCase):
         path = "/rooms/%s/send/m.room.message/mid1" % (
             urllib.quote(self.room_id))
         # missing keys or invalid json
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           path, '{}')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", path, '{}'
+        )
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           path, '{"_name":"bob"}')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", path, '{"_name":"bob"}'
+        )
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           path, '{"nao')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", path, '{"nao'
+        )
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           path, '[{"_name":"bob"},{"_name":"jill"}]')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", path, '[{"_name":"bob"},{"_name":"jill"}]'
+        )
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           path, 'text only')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", path, 'text only'
+        )
         self.assertEquals(400, code, msg=str(response))
 
-        (code, response) = yield self.mock_resource.trigger("PUT",
-                           path, '')
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", path, ''
+        )
         self.assertEquals(400, code, msg=str(response))
 
     @defer.inlineCallbacks
@@ -953,19 +956,14 @@ class RoomInitialSyncTestCase(RestTestCase):
 
         synapse.rest.client.v1.room.register_servlets(hs, self.mock_resource)
 
-        # Since I'm getting my own presence I need to exist as far as presence
-        # is concerned.
-        hs.get_handlers().presence_handler.registered_user(
-            UserID.from_string(self.user_id)
-        )
-
         # create the room
         self.room_id = yield self.create_room_as(self.user_id)
 
     @defer.inlineCallbacks
     def test_initial_sync(self):
         (code, response) = yield self.mock_resource.trigger_get(
-                "/rooms/%s/initialSync" % self.room_id)
+            "/rooms/%s/initialSync" % self.room_id
+        )
         self.assertEquals(200, code)
 
         self.assertEquals(self.room_id, response["room_id"])
@@ -989,8 +987,8 @@ class RoomInitialSyncTestCase(RestTestCase):
 
         self.assertTrue("presence" in response)
 
-        presence_by_user = {e["content"]["user_id"]: e
-            for e in response["presence"]
+        presence_by_user = {
+            e["content"]["user_id"]: e for e in response["presence"]
         }
         self.assertTrue(self.user_id in presence_by_user)
         self.assertEquals("m.presence", presence_by_user[self.user_id]["type"])
diff --git a/tests/rest/client/v1/test_typing.py b/tests/rest/client/v1/test_typing.py
index c4ac181a33..16d788ff61 100644
--- a/tests/rest/client/v1/test_typing.py
+++ b/tests/rest/client/v1/test_typing.py
@@ -81,9 +81,9 @@ class RoomTypingTestCase(RestTestCase):
                 return defer.succeed([])
 
         @defer.inlineCallbacks
-        def fetch_room_distributions_into(room_id, localusers=None,
-                remotedomains=None, ignore_user=None):
-
+        def fetch_room_distributions_into(
+            room_id, localusers=None, remotedomains=None, ignore_user=None
+        ):
             members = yield get_room_members(room_id)
             for member in members:
                 if ignore_user is not None and member == ignore_user:
@@ -96,7 +96,8 @@ class RoomTypingTestCase(RestTestCase):
                     if remotedomains is not None:
                         remotedomains.add(member.domain)
         hs.get_handlers().room_member_handler.fetch_room_distributions_into = (
-                fetch_room_distributions_into)
+            fetch_room_distributions_into
+        )
 
         synapse.rest.client.v1.room.register_servlets(hs, self.mock_resource)
 
@@ -109,8 +110,8 @@ class RoomTypingTestCase(RestTestCase):
 
     @defer.inlineCallbacks
     def test_set_typing(self):
-        (code, _) = yield self.mock_resource.trigger("PUT",
-            "/rooms/%s/typing/%s" % (self.room_id, self.user_id),
+        (code, _) = yield self.mock_resource.trigger(
+            "PUT", "/rooms/%s/typing/%s" % (self.room_id, self.user_id),
             '{"typing": true, "timeout": 30000}'
         )
         self.assertEquals(200, code)
@@ -120,41 +121,38 @@ class RoomTypingTestCase(RestTestCase):
             from_key=0,
             room_ids=[self.room_id],
         )
-        self.assertEquals(
-            events[0],
-            [
-                {"type": "m.typing",
-                 "room_id": self.room_id,
-                 "content": {
-                     "user_ids": [self.user_id],
-                }},
-            ]
-        )
+        self.assertEquals(events[0], [{
+            "type": "m.typing",
+            "room_id": self.room_id,
+            "content": {
+                "user_ids": [self.user_id],
+            }
+        }])
 
     @defer.inlineCallbacks
     def test_set_not_typing(self):
-        (code, _) = yield self.mock_resource.trigger("PUT",
-            "/rooms/%s/typing/%s" % (self.room_id, self.user_id),
+        (code, _) = yield self.mock_resource.trigger(
+            "PUT", "/rooms/%s/typing/%s" % (self.room_id, self.user_id),
             '{"typing": false}'
         )
         self.assertEquals(200, code)
 
     @defer.inlineCallbacks
     def test_typing_timeout(self):
-        (code, _) = yield self.mock_resource.trigger("PUT",
-            "/rooms/%s/typing/%s" % (self.room_id, self.user_id),
+        (code, _) = yield self.mock_resource.trigger(
+            "PUT", "/rooms/%s/typing/%s" % (self.room_id, self.user_id),
             '{"typing": true, "timeout": 30000}'
         )
         self.assertEquals(200, code)
 
         self.assertEquals(self.event_source.get_current_key(), 1)
 
-        self.clock.advance_time(31);
+        self.clock.advance_time(31)
 
         self.assertEquals(self.event_source.get_current_key(), 2)
 
-        (code, _) = yield self.mock_resource.trigger("PUT",
-            "/rooms/%s/typing/%s" % (self.room_id, self.user_id),
+        (code, _) = yield self.mock_resource.trigger(
+            "PUT", "/rooms/%s/typing/%s" % (self.room_id, self.user_id),
             '{"typing": true, "timeout": 30000}'
         )
         self.assertEquals(200, code)
diff --git a/tests/rest/client/v1/utils.py b/tests/rest/client/v1/utils.py
index af376804f6..17524b2e23 100644
--- a/tests/rest/client/v1/utils.py
+++ b/tests/rest/client/v1/utils.py
@@ -84,8 +84,9 @@ class RestTestCase(unittest.TestCase):
             "membership": membership
         }
 
-        (code, response) = yield self.mock_resource.trigger("PUT", path,
-            json.dumps(data))
+        (code, response) = yield self.mock_resource.trigger(
+            "PUT", path, json.dumps(data)
+        )
         self.assertEquals(expect_code, code, msg=str(response))
 
         self.auth_user_id = temp_id
diff --git a/tests/rest/client/v2_alpha/__init__.py b/tests/rest/client/v2_alpha/__init__.py
index 16dce6c723..84334dce34 100644
--- a/tests/rest/client/v2_alpha/__init__.py
+++ b/tests/rest/client/v2_alpha/__init__.py
@@ -55,7 +55,7 @@ class V2AlphaRestTestCase(unittest.TestCase):
             r.register_servlets(hs, self.mock_resource)
 
     def make_datastore_mock(self):
-        store =  Mock(spec=[
+        store = Mock(spec=[
             "insert_client_ip",
         ])
         store.get_app_service_by_token = Mock(return_value=None)
diff --git a/tests/rest/client/v2_alpha/test_filter.py b/tests/rest/client/v2_alpha/test_filter.py
index c86e904c8e..d1442aafac 100644
--- a/tests/rest/client/v2_alpha/test_filter.py
+++ b/tests/rest/client/v2_alpha/test_filter.py
@@ -15,8 +15,6 @@
 
 from twisted.internet import defer
 
-from mock import Mock
-
 from . import V2AlphaRestTestCase
 
 from synapse.rest.client.v2_alpha import filter
@@ -53,9 +51,8 @@ class FilterTestCase(V2AlphaRestTestCase):
 
     @defer.inlineCallbacks
     def test_add_filter(self):
-        (code, response) = yield self.mock_resource.trigger("POST",
-            "/user/%s/filter" % (self.USER_ID),
-            '{"type": ["m.*"]}'
+        (code, response) = yield self.mock_resource.trigger(
+            "POST", "/user/%s/filter" % (self.USER_ID), '{"type": ["m.*"]}'
         )
         self.assertEquals(200, code)
         self.assertEquals({"filter_id": "0"}, response)
@@ -70,8 +67,8 @@ class FilterTestCase(V2AlphaRestTestCase):
             {"type": ["m.*"]}
         ]
 
-        (code, response) = yield self.mock_resource.trigger("GET",
-            "/user/%s/filter/0" % (self.USER_ID), None
+        (code, response) = yield self.mock_resource.trigger_get(
+            "/user/%s/filter/0" % (self.USER_ID)
         )
         self.assertEquals(200, code)
         self.assertEquals({"type": ["m.*"]}, response)
@@ -82,14 +79,14 @@ class FilterTestCase(V2AlphaRestTestCase):
             {"type": ["m.*"]}
         ]
 
-        (code, response) = yield self.mock_resource.trigger("GET",
-            "/user/%s/filter/2" % (self.USER_ID), None
+        (code, response) = yield self.mock_resource.trigger_get(
+            "/user/%s/filter/2" % (self.USER_ID)
         )
         self.assertEquals(404, code)
 
     @defer.inlineCallbacks
     def test_get_filter_no_user(self):
-        (code, response) = yield self.mock_resource.trigger("GET",
-            "/user/%s/filter/0" % (self.USER_ID), None
+        (code, response) = yield self.mock_resource.trigger_get(
+            "/user/%s/filter/0" % (self.USER_ID)
         )
         self.assertEquals(404, code)
diff --git a/tests/rest/client/v2_alpha/test_register.py b/tests/rest/client/v2_alpha/test_register.py
index df0841b0b1..b867599079 100644
--- a/tests/rest/client/v2_alpha/test_register.py
+++ b/tests/rest/client/v2_alpha/test_register.py
@@ -1,7 +1,7 @@
 from synapse.rest.client.v2_alpha.register import RegisterRestServlet
 from synapse.api.errors import SynapseError
 from twisted.internet import defer
-from mock import Mock, MagicMock
+from mock import Mock
 from tests import unittest
 import json
 
@@ -24,7 +24,7 @@ class RegisterRestServletTestCase(unittest.TestCase):
 
         self.auth_result = (False, None, None)
         self.auth_handler = Mock(
-            check_auth=Mock(side_effect=lambda x,y,z: self.auth_result)
+            check_auth=Mock(side_effect=lambda x, y, z: self.auth_result)
         )
         self.registration_handler = Mock()
         self.identity_handler = Mock()
diff --git a/tests/storage/event_injector.py b/tests/storage/event_injector.py
index dca785eb27..f22ba8db89 100644
--- a/tests/storage/event_injector.py
+++ b/tests/storage/event_injector.py
@@ -14,15 +14,9 @@
 # limitations under the License.
 
 
-from tests import unittest
 from twisted.internet import defer
 
-from synapse.api.constants import EventTypes, Membership
-from synapse.types import UserID, RoomID
-
-from tests.utils import setup_test_homeserver
-
-from mock import Mock
+from synapse.api.constants import EventTypes
 
 
 class EventInjector:
diff --git a/tests/storage/test__base.py b/tests/storage/test__base.py
index 219288621d..96b7dba5fe 100644
--- a/tests/storage/test__base.py
+++ b/tests/storage/test__base.py
@@ -174,11 +174,13 @@ class CacheDecoratorTestCase(unittest.TestCase):
 
         # There must have been at least 2 evictions, meaning if we calculate
         # all 12 values again, we must get called at least 2 more times
-        for k in range(0,12):
+        for k in range(0, 12):
             yield a.func(k)
 
-        self.assertTrue(callcount[0] >= 14,
-            msg="Expected callcount >= 14, got %d" % (callcount[0]))
+        self.assertTrue(
+            callcount[0] >= 14,
+            msg="Expected callcount >= 14, got %d" % (callcount[0])
+        )
 
     def test_prefill(self):
         callcount = [0]
diff --git a/tests/storage/test_appservice.py b/tests/storage/test_appservice.py
index ed8af10d87..5734198121 100644
--- a/tests/storage/test_appservice.py
+++ b/tests/storage/test_appservice.py
@@ -35,7 +35,8 @@ class ApplicationServiceStoreTestCase(unittest.TestCase):
     def setUp(self):
         self.as_yaml_files = []
         config = Mock(
-            app_service_config_files=self.as_yaml_files
+            app_service_config_files=self.as_yaml_files,
+            event_cache_size=1,
         )
         hs = yield setup_test_homeserver(config=config)
 
@@ -109,7 +110,8 @@ class ApplicationServiceTransactionStoreTestCase(unittest.TestCase):
         self.as_yaml_files = []
 
         config = Mock(
-            app_service_config_files=self.as_yaml_files
+            app_service_config_files=self.as_yaml_files,
+            event_cache_size=1,
         )
         hs = yield setup_test_homeserver(config=config)
         self.db_pool = hs.get_db_pool()
@@ -438,7 +440,7 @@ class ApplicationServiceStoreConfigTestCase(unittest.TestCase):
         f1 = self._write_config(suffix="1")
         f2 = self._write_config(suffix="2")
 
-        config = Mock(app_service_config_files=[f1, f2])
+        config = Mock(app_service_config_files=[f1, f2], event_cache_size=1)
         hs = yield setup_test_homeserver(config=config, datastore=Mock())
 
         ApplicationServiceStore(hs)
@@ -448,7 +450,7 @@ class ApplicationServiceStoreConfigTestCase(unittest.TestCase):
         f1 = self._write_config(id="id", suffix="1")
         f2 = self._write_config(id="id", suffix="2")
 
-        config = Mock(app_service_config_files=[f1, f2])
+        config = Mock(app_service_config_files=[f1, f2], event_cache_size=1)
         hs = yield setup_test_homeserver(config=config, datastore=Mock())
 
         with self.assertRaises(ConfigError) as cm:
@@ -464,7 +466,7 @@ class ApplicationServiceStoreConfigTestCase(unittest.TestCase):
         f1 = self._write_config(as_token="as_token", suffix="1")
         f2 = self._write_config(as_token="as_token", suffix="2")
 
-        config = Mock(app_service_config_files=[f1, f2])
+        config = Mock(app_service_config_files=[f1, f2], event_cache_size=1)
         hs = yield setup_test_homeserver(config=config, datastore=Mock())
 
         with self.assertRaises(ConfigError) as cm:
diff --git a/tests/storage/test_background_update.py b/tests/storage/test_background_update.py
index 29289fa9b4..6e4d9b1373 100644
--- a/tests/storage/test_background_update.py
+++ b/tests/storage/test_background_update.py
@@ -1,13 +1,11 @@
 from tests import unittest
 from twisted.internet import defer
 
-from synapse.api.constants import EventTypes
-from synapse.types import UserID, RoomID, RoomAlias
-
 from tests.utils import setup_test_homeserver
 
 from mock import Mock
 
+
 class BackgroundUpdateTestCase(unittest.TestCase):
 
     @defer.inlineCallbacks
@@ -24,8 +22,8 @@ class BackgroundUpdateTestCase(unittest.TestCase):
 
     @defer.inlineCallbacks
     def test_do_background_update(self):
-        desired_count = 1000;
-        duration_ms = 42;
+        desired_count = 1000
+        duration_ms = 42
 
         @defer.inlineCallbacks
         def update(progress, count):
diff --git a/tests/storage/test_base.py b/tests/storage/test_base.py
index 0684fb6f70..2e33beb07c 100644
--- a/tests/storage/test_base.py
+++ b/tests/storage/test_base.py
@@ -17,7 +17,7 @@
 from tests import unittest
 from twisted.internet import defer
 
-from mock import Mock, call
+from mock import Mock
 
 from collections import OrderedDict
 
@@ -63,13 +63,12 @@ class SQLBaseStoreTestCase(unittest.TestCase):
         self.mock_txn.rowcount = 1
 
         yield self.datastore._simple_insert(
-                table="tablename",
-                values={"columname": "Value"}
+            table="tablename",
+            values={"columname": "Value"}
         )
 
         self.mock_txn.execute.assert_called_with(
-                "INSERT INTO tablename (columname) VALUES(?)",
-                ("Value",)
+            "INSERT INTO tablename (columname) VALUES(?)", ("Value",)
         )
 
     @defer.inlineCallbacks
@@ -77,14 +76,14 @@ class SQLBaseStoreTestCase(unittest.TestCase):
         self.mock_txn.rowcount = 1
 
         yield self.datastore._simple_insert(
-                table="tablename",
-                # Use OrderedDict() so we can assert on the SQL generated
-                values=OrderedDict([("colA", 1), ("colB", 2), ("colC", 3)])
+            table="tablename",
+            # Use OrderedDict() so we can assert on the SQL generated
+            values=OrderedDict([("colA", 1), ("colB", 2), ("colC", 3)])
         )
 
         self.mock_txn.execute.assert_called_with(
-                "INSERT INTO tablename (colA, colB, colC) VALUES(?, ?, ?)",
-                (1, 2, 3,)
+            "INSERT INTO tablename (colA, colB, colC) VALUES(?, ?, ?)",
+            (1, 2, 3,)
         )
 
     @defer.inlineCallbacks
@@ -93,15 +92,14 @@ class SQLBaseStoreTestCase(unittest.TestCase):
         self.mock_txn.fetchall.return_value = [("Value",)]
 
         value = yield self.datastore._simple_select_one_onecol(
-                table="tablename",
-                keyvalues={"keycol": "TheKey"},
-                retcol="retcol"
+            table="tablename",
+            keyvalues={"keycol": "TheKey"},
+            retcol="retcol"
         )
 
         self.assertEquals("Value", value)
         self.mock_txn.execute.assert_called_with(
-                "SELECT retcol FROM tablename WHERE keycol = ?",
-                ["TheKey"]
+            "SELECT retcol FROM tablename WHERE keycol = ?", ["TheKey"]
         )
 
     @defer.inlineCallbacks
@@ -110,15 +108,15 @@ class SQLBaseStoreTestCase(unittest.TestCase):
         self.mock_txn.fetchone.return_value = (1, 2, 3)
 
         ret = yield self.datastore._simple_select_one(
-                table="tablename",
-                keyvalues={"keycol": "TheKey"},
-                retcols=["colA", "colB", "colC"]
+            table="tablename",
+            keyvalues={"keycol": "TheKey"},
+            retcols=["colA", "colB", "colC"]
         )
 
         self.assertEquals({"colA": 1, "colB": 2, "colC": 3}, ret)
         self.mock_txn.execute.assert_called_with(
-                "SELECT colA, colB, colC FROM tablename WHERE keycol = ?",
-                ["TheKey"]
+            "SELECT colA, colB, colC FROM tablename WHERE keycol = ?",
+            ["TheKey"]
         )
 
     @defer.inlineCallbacks
@@ -127,32 +125,32 @@ class SQLBaseStoreTestCase(unittest.TestCase):
         self.mock_txn.fetchone.return_value = None
 
         ret = yield self.datastore._simple_select_one(
-                table="tablename",
-                keyvalues={"keycol": "Not here"},
-                retcols=["colA"],
-                allow_none=True
+            table="tablename",
+            keyvalues={"keycol": "Not here"},
+            retcols=["colA"],
+            allow_none=True
         )
 
         self.assertFalse(ret)
 
     @defer.inlineCallbacks
     def test_select_list(self):
-        self.mock_txn.rowcount = 3;
+        self.mock_txn.rowcount = 3
         self.mock_txn.fetchall.return_value = ((1,), (2,), (3,))
         self.mock_txn.description = (
-                ("colA", None, None, None, None, None, None),
+            ("colA", None, None, None, None, None, None),
         )
 
         ret = yield self.datastore._simple_select_list(
-                table="tablename",
-                keyvalues={"keycol": "A set"},
-                retcols=["colA"],
+            table="tablename",
+            keyvalues={"keycol": "A set"},
+            retcols=["colA"],
         )
 
         self.assertEquals([{"colA": 1}, {"colA": 2}, {"colA": 3}], ret)
         self.mock_txn.execute.assert_called_with(
-                "SELECT colA FROM tablename WHERE keycol = ?",
-                ["A set"]
+            "SELECT colA FROM tablename WHERE keycol = ?",
+            ["A set"]
         )
 
     @defer.inlineCallbacks
@@ -160,14 +158,14 @@ class SQLBaseStoreTestCase(unittest.TestCase):
         self.mock_txn.rowcount = 1
 
         yield self.datastore._simple_update_one(
-                table="tablename",
-                keyvalues={"keycol": "TheKey"},
-                updatevalues={"columnname": "New Value"}
+            table="tablename",
+            keyvalues={"keycol": "TheKey"},
+            updatevalues={"columnname": "New Value"}
         )
 
         self.mock_txn.execute.assert_called_with(
-                "UPDATE tablename SET columnname = ? WHERE keycol = ?",
-                ["New Value", "TheKey"]
+            "UPDATE tablename SET columnname = ? WHERE keycol = ?",
+            ["New Value", "TheKey"]
         )
 
     @defer.inlineCallbacks
@@ -175,15 +173,15 @@ class SQLBaseStoreTestCase(unittest.TestCase):
         self.mock_txn.rowcount = 1
 
         yield self.datastore._simple_update_one(
-                table="tablename",
-                keyvalues=OrderedDict([("colA", 1), ("colB", 2)]),
-                updatevalues=OrderedDict([("colC", 3), ("colD", 4)])
+            table="tablename",
+            keyvalues=OrderedDict([("colA", 1), ("colB", 2)]),
+            updatevalues=OrderedDict([("colC", 3), ("colD", 4)])
         )
 
         self.mock_txn.execute.assert_called_with(
-                "UPDATE tablename SET colC = ?, colD = ? WHERE " +
-                    "colA = ? AND colB = ?",
-                [3, 4, 1, 2]
+            "UPDATE tablename SET colC = ?, colD = ? WHERE"
+            " colA = ? AND colB = ?",
+            [3, 4, 1, 2]
         )
 
     @defer.inlineCallbacks
@@ -191,11 +189,10 @@ class SQLBaseStoreTestCase(unittest.TestCase):
         self.mock_txn.rowcount = 1
 
         yield self.datastore._simple_delete_one(
-                table="tablename",
-                keyvalues={"keycol": "Go away"},
+            table="tablename",
+            keyvalues={"keycol": "Go away"},
         )
 
         self.mock_txn.execute.assert_called_with(
-                "DELETE FROM tablename WHERE keycol = ?",
-                ["Go away"]
+            "DELETE FROM tablename WHERE keycol = ?", ["Go away"]
         )
diff --git a/tests/storage/test_events.py b/tests/storage/test_events.py
index 4aa82d4c9d..18a6cff0c7 100644
--- a/tests/storage/test_events.py
+++ b/tests/storage/test_events.py
@@ -12,7 +12,6 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-import uuid
 from mock import Mock
 from synapse.types import RoomID, UserID
 
diff --git a/tests/storage/test_presence.py b/tests/storage/test_presence.py
index 333f1e10f1..ec78f007ca 100644
--- a/tests/storage/test_presence.py
+++ b/tests/storage/test_presence.py
@@ -35,32 +35,6 @@ class PresenceStoreTestCase(unittest.TestCase):
         self.u_banana = UserID.from_string("@banana:test")
 
     @defer.inlineCallbacks
-    def test_state(self):
-        yield self.store.create_presence(
-            self.u_apple.localpart
-        )
-
-        state = yield self.store.get_presence_state(
-            self.u_apple.localpart
-        )
-
-        self.assertEquals(
-            {"state": None, "status_msg": None, "mtime": None}, state
-        )
-
-        yield self.store.set_presence_state(
-            self.u_apple.localpart, {"state": "online", "status_msg": "Here"}
-        )
-
-        state = yield self.store.get_presence_state(
-            self.u_apple.localpart
-        )
-
-        self.assertEquals(
-            {"state": "online", "status_msg": "Here", "mtime": 1000000}, state
-        )
-
-    @defer.inlineCallbacks
     def test_visibility(self):
         self.assertFalse((yield self.store.is_presence_visible(
             observed_localpart=self.u_apple.localpart,
diff --git a/tests/storage/test_profile.py b/tests/storage/test_profile.py
index 47e2768b2c..24118bbc86 100644
--- a/tests/storage/test_profile.py
+++ b/tests/storage/test_profile.py
@@ -55,7 +55,7 @@ class ProfileStoreTestCase(unittest.TestCase):
         )
 
         yield self.store.set_profile_avatar_url(
-                self.u_frank.localpart, "http://my.site/here"
+            self.u_frank.localpart, "http://my.site/here"
         )
 
         self.assertEquals(
diff --git a/tests/storage/test_registration.py b/tests/storage/test_registration.py
index 7b3b4c13bc..b8384c98d8 100644
--- a/tests/storage/test_registration.py
+++ b/tests/storage/test_registration.py
@@ -33,8 +33,10 @@ class RegistrationStoreTestCase(unittest.TestCase):
         self.store = hs.get_datastore()
 
         self.user_id = "@my-user:test"
-        self.tokens = ["AbCdEfGhIjKlMnOpQrStUvWxYz",
-                        "BcDeFgHiJkLmNoPqRsTuVwXyZa"]
+        self.tokens = [
+            "AbCdEfGhIjKlMnOpQrStUvWxYz",
+            "BcDeFgHiJkLmNoPqRsTuVwXyZa"
+        ]
         self.pwhash = "{xx1}123456789"
 
     @defer.inlineCallbacks
diff --git a/tests/storage/test_room.py b/tests/storage/test_room.py
index 0baaf3df21..ef8a4d234f 100644
--- a/tests/storage/test_room.py
+++ b/tests/storage/test_room.py
@@ -37,7 +37,8 @@ class RoomStoreTestCase(unittest.TestCase):
         self.alias = RoomAlias.from_string("#a-room-name:test")
         self.u_creator = UserID.from_string("@creator:test")
 
-        yield self.store.store_room(self.room.to_string(),
+        yield self.store.store_room(
+            self.room.to_string(),
             room_creator_user_id=self.u_creator.to_string(),
             is_public=True
         )
@@ -45,9 +46,11 @@ class RoomStoreTestCase(unittest.TestCase):
     @defer.inlineCallbacks
     def test_get_room(self):
         self.assertDictContainsSubset(
-            {"room_id": self.room.to_string(),
-             "creator": self.u_creator.to_string(),
-             "is_public": True},
+            {
+                "room_id": self.room.to_string(),
+                "creator": self.u_creator.to_string(),
+                "is_public": True
+            },
             (yield self.store.get_room(self.room.to_string()))
         )
 
@@ -65,7 +68,8 @@ class RoomEventsStoreTestCase(unittest.TestCase):
 
         self.room = RoomID.from_string("!abcde:test")
 
-        yield self.store.store_room(self.room.to_string(),
+        yield self.store.store_room(
+            self.room.to_string(),
             room_creator_user_id="@creator:text",
             is_public=True
         )
diff --git a/tests/storage/test_roommember.py b/tests/storage/test_roommember.py
index bab15c4165..677d11f68d 100644
--- a/tests/storage/test_roommember.py
+++ b/tests/storage/test_roommember.py
@@ -88,8 +88,8 @@ class RoomMemberStoreTestCase(unittest.TestCase):
             [m.room_id for m in (
                 yield self.store.get_rooms_for_user_where_membership_is(
                     self.u_alice.to_string(), [Membership.JOIN]
-                ))
-            ]
+                )
+            )]
         )
         self.assertFalse(
             (yield self.store.user_rooms_intersect(
@@ -108,11 +108,11 @@ class RoomMemberStoreTestCase(unittest.TestCase):
                 yield self.store.get_room_members(self.room.to_string())
             )}
         )
-        self.assertTrue(
-            (yield self.store.user_rooms_intersect(
-                [self.u_alice.to_string(), self.u_bob.to_string()]
-            ))
-        )
+        self.assertTrue((
+            yield self.store.user_rooms_intersect([
+                self.u_alice.to_string(), self.u_bob.to_string()
+            ])
+        ))
 
     @defer.inlineCallbacks
     def test_room_hosts(self):
@@ -136,9 +136,7 @@ class RoomMemberStoreTestCase(unittest.TestCase):
 
         self.assertEquals(
             {"test", "elsewhere"},
-            (yield
-                self.store.get_joined_hosts_for_room(self.room.to_string())
-            )
+            (yield self.store.get_joined_hosts_for_room(self.room.to_string()))
         )
 
         # Should still have both hosts
@@ -146,9 +144,7 @@ class RoomMemberStoreTestCase(unittest.TestCase):
 
         self.assertEquals(
             {"test", "elsewhere"},
-            (yield
-                self.store.get_joined_hosts_for_room(self.room.to_string())
-            )
+            (yield self.store.get_joined_hosts_for_room(self.room.to_string()))
         )
 
         # Should have only one host after other leaves
diff --git a/tests/storage/test_stream.py b/tests/storage/test_stream.py
index 708208aff1..da322152c7 100644
--- a/tests/storage/test_stream.py
+++ b/tests/storage/test_stream.py
@@ -156,13 +156,13 @@ class StreamStoreTestCase(unittest.TestCase):
             self.room1, self.u_bob, Membership.JOIN
         )
 
-        event1 = yield self.event_injector.inject_room_member(
+        yield self.event_injector.inject_room_member(
             self.room1, self.u_alice, Membership.JOIN
         )
 
         start = yield self.store.get_room_events_max_id()
 
-        event2 = yield self.event_injector.inject_room_member(
+        yield self.event_injector.inject_room_member(
             self.room1, self.u_alice, Membership.JOIN,
         )
 
diff --git a/tests/test_distributor.py b/tests/test_distributor.py
index a80f580ba6..acebcf4a86 100644
--- a/tests/test_distributor.py
+++ b/tests/test_distributor.py
@@ -44,8 +44,10 @@ class DistributorTestCase(unittest.TestCase):
         self.dist.declare("whine")
 
         d_inner = defer.Deferred()
+
         def observer():
             return d_inner
+
         self.dist.observe("whine", observer)
 
         d_outer = self.dist.fire("whine")
@@ -66,8 +68,8 @@ class DistributorTestCase(unittest.TestCase):
 
         observers[0].side_effect = Exception("Awoogah!")
 
-        with patch("synapse.util.distributor.logger",
-                spec=["warning"]
+        with patch(
+            "synapse.util.distributor.logger", spec=["warning"]
         ) as mock_logger:
             d = self.dist.fire("alarm", "Go")
             yield d
@@ -77,8 +79,9 @@ class DistributorTestCase(unittest.TestCase):
             observers[1].assert_called_once_with("Go")
 
             self.assertEquals(mock_logger.warning.call_count, 1)
-            self.assertIsInstance(mock_logger.warning.call_args[0][0],
-                    str)
+            self.assertIsInstance(
+                mock_logger.warning.call_args[0][0], str
+            )
 
     @defer.inlineCallbacks
     def test_signal_catch_no_suppress(self):
diff --git a/tests/test_test_utils.py b/tests/test_test_utils.py
index 3718f4fc3f..d28bb726bb 100644
--- a/tests/test_test_utils.py
+++ b/tests/test_test_utils.py
@@ -17,6 +17,7 @@ from tests import unittest
 
 from tests.utils import MockClock
 
+
 class MockClockTestCase(unittest.TestCase):
 
     def setUp(self):
@@ -60,7 +61,7 @@ class MockClockTestCase(unittest.TestCase):
 
         def _cb1():
             invoked[1] = 1
-        t1 = self.clock.call_later(20, _cb1)
+        self.clock.call_later(20, _cb1)
 
         self.clock.cancel_call_later(t0)
 
diff --git a/tests/unittest.py b/tests/unittest.py
index 6f02eb4cac..5b22abfe74 100644
--- a/tests/unittest.py
+++ b/tests/unittest.py
@@ -37,9 +37,12 @@ def around(target):
     def _around(code):
         name = code.__name__
         orig = getattr(target, name)
+
         def new(*args, **kwargs):
             return code(orig, *args, **kwargs)
+
         setattr(target, name, new)
+
     return _around
 
 
@@ -53,9 +56,7 @@ class TestCase(unittest.TestCase):
 
         method = getattr(self, methodName)
 
-        level = getattr(method, "loglevel",
-                    getattr(self, "loglevel",
-                        NEVER))
+        level = getattr(method, "loglevel", getattr(self, "loglevel", NEVER))
 
         @around(self)
         def setUp(orig):
diff --git a/tests/util/__init__.py b/tests/util/__init__.py
index d0e9399dda..bfebb0f644 100644
--- a/tests/util/__init__.py
+++ b/tests/util/__init__.py
@@ -12,4 +12,3 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
diff --git a/tests/util/test_dict_cache.py b/tests/util/test_dict_cache.py
index 7bbe795622..272b71034a 100644
--- a/tests/util/test_dict_cache.py
+++ b/tests/util/test_dict_cache.py
@@ -14,7 +14,6 @@
 # limitations under the License.
 
 
-from twisted.internet import defer
 from tests import unittest
 
 from synapse.util.caches.dictionary_cache import DictionaryCache
diff --git a/tests/util/test_snapshot_cache.py b/tests/util/test_snapshot_cache.py
index 4ee0d49673..7e289715ba 100644
--- a/tests/util/test_snapshot_cache.py
+++ b/tests/util/test_snapshot_cache.py
@@ -19,6 +19,7 @@ from .. import unittest
 from synapse.util.caches.snapshot_cache import SnapshotCache
 from twisted.internet.defer import Deferred
 
+
 class SnapshotCacheTestCase(unittest.TestCase):
 
     def setUp(self):
diff --git a/tests/util/test_treecache.py b/tests/util/test_treecache.py
index 1efbeb6b33..7ab578a185 100644
--- a/tests/util/test_treecache.py
+++ b/tests/util/test_treecache.py
@@ -18,6 +18,7 @@ from .. import unittest
 
 from synapse.util.caches.treecache import TreeCache
 
+
 class TreeCacheTestCase(unittest.TestCase):
     def test_get_set_onelevel(self):
         cache = TreeCache()
@@ -76,3 +77,9 @@ class TreeCacheTestCase(unittest.TestCase):
         cache[("b",)] = "B"
         cache.clear()
         self.assertEquals(len(cache), 0)
+
+    def test_contains(self):
+        cache = TreeCache()
+        cache[("a",)] = "A"
+        self.assertTrue(("a",) in cache)
+        self.assertFalse(("b",) in cache)
diff --git a/tests/util/test_wheel_timer.py b/tests/util/test_wheel_timer.py
new file mode 100644
index 0000000000..c44567e52e
--- /dev/null
+++ b/tests/util/test_wheel_timer.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+# Copyright 2016 OpenMarket Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from .. import unittest
+
+from synapse.util.wheel_timer import WheelTimer
+
+
+class WheelTimerTestCase(unittest.TestCase):
+    def test_single_insert_fetch(self):
+        wheel = WheelTimer(bucket_size=5)
+
+        obj = object()
+        wheel.insert(100, obj, 150)
+
+        self.assertListEqual(wheel.fetch(101), [])
+        self.assertListEqual(wheel.fetch(110), [])
+        self.assertListEqual(wheel.fetch(120), [])
+        self.assertListEqual(wheel.fetch(130), [])
+        self.assertListEqual(wheel.fetch(149), [])
+        self.assertListEqual(wheel.fetch(156), [obj])
+        self.assertListEqual(wheel.fetch(170), [])
+
+    def test_mutli_insert(self):
+        wheel = WheelTimer(bucket_size=5)
+
+        obj1 = object()
+        obj2 = object()
+        obj3 = object()
+        wheel.insert(100, obj1, 150)
+        wheel.insert(105, obj2, 130)
+        wheel.insert(106, obj3, 160)
+
+        self.assertListEqual(wheel.fetch(110), [])
+        self.assertListEqual(wheel.fetch(135), [obj2])
+        self.assertListEqual(wheel.fetch(149), [])
+        self.assertListEqual(wheel.fetch(158), [obj1])
+        self.assertListEqual(wheel.fetch(160), [])
+        self.assertListEqual(wheel.fetch(200), [obj3])
+        self.assertListEqual(wheel.fetch(210), [])
+
+    def test_insert_past(self):
+        wheel = WheelTimer(bucket_size=5)
+
+        obj = object()
+        wheel.insert(100, obj, 50)
+        self.assertListEqual(wheel.fetch(120), [obj])
+
+    def test_insert_past_mutli(self):
+        wheel = WheelTimer(bucket_size=5)
+
+        obj1 = object()
+        obj2 = object()
+        obj3 = object()
+        wheel.insert(100, obj1, 150)
+        wheel.insert(100, obj2, 140)
+        wheel.insert(100, obj3, 50)
+        self.assertListEqual(wheel.fetch(110), [obj3])
+        self.assertListEqual(wheel.fetch(120), [])
+        self.assertListEqual(wheel.fetch(147), [obj2])
+        self.assertListEqual(wheel.fetch(200), [obj1])
+        self.assertListEqual(wheel.fetch(240), [])
diff --git a/tests/utils.py b/tests/utils.py
index f3935648a0..c67fa1ca35 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -154,7 +154,7 @@ class MockHttpResource(HttpServer):
 
         mock_request.getClientIP.return_value = "-"
 
-        mock_request.requestHeaders.getRawHeaders.return_value=[
+        mock_request.requestHeaders.getRawHeaders.return_value = [
             "X-Matrix origin=test,key=,sig="
         ]
 
@@ -226,12 +226,12 @@ class MockClock(object):
     def time_msec(self):
         return self.time() * 1000
 
-    def call_later(self, delay, callback):
+    def call_later(self, delay, callback, *args, **kwargs):
         current_context = LoggingContext.current_context()
 
         def wrapped_callback():
             LoggingContext.thread_local.current_context = current_context
-            callback()
+            callback(*args, **kwargs)
 
         t = [self.now + delay, wrapped_callback, False]
         self.timers.append(t)
@@ -241,9 +241,10 @@ class MockClock(object):
     def looping_call(self, function, interval):
         pass
 
-    def cancel_call_later(self, timer):
+    def cancel_call_later(self, timer, ignore_errs=False):
         if timer[2]:
-            raise Exception("Cannot cancel an expired timer")
+            if not ignore_errs:
+                raise Exception("Cannot cancel an expired timer")
 
         timer[2] = True
         self.timers = [t for t in self.timers if t != timer]
@@ -368,13 +369,12 @@ class MemoryDataStore(object):
 
     def get_rooms_for_user_where_membership_is(self, user_id, membership_list):
         return [
-            self.members[r].get(user_id) for r in self.members
-            if user_id in self.members[r] and
-                self.members[r][user_id].membership in membership_list
+            m[user_id] for m in self.members.values()
+            if user_id in m and m[user_id].membership in membership_list
         ]
 
     def get_room_events_stream(self, user_id=None, from_key=None, to_key=None,
-                            limit=0, with_feedback=False):
+                               limit=0, with_feedback=False):
         return ([], from_key)  # TODO
 
     def get_joined_hosts_for_room(self, room_id):
@@ -384,7 +384,6 @@ class MemoryDataStore(object):
         if event.type == EventTypes.Member:
             room_id = event.room_id
             user = event.state_key
-            membership = event.membership
             self.members.setdefault(room_id, {})[user] = event
 
         if hasattr(event, "state_key"):
@@ -464,9 +463,9 @@ class DeferredMockCallable(object):
                 d.callback(None)
                 return result
 
-        failure = AssertionError("Was not expecting call(%s)" %
+        failure = AssertionError("Was not expecting call(%s)" % (
             _format_call(args, kwargs)
-        )
+        ))
 
         for _, _, d in self.expectations:
             try:
@@ -487,14 +486,12 @@ class DeferredMockCallable(object):
         )
 
         timer = reactor.callLater(
-            timeout/1000,
+            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]
-                )
-            )
+            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
@@ -508,8 +505,8 @@ class DeferredMockCallable(object):
             calls = self.calls
             self.calls = []
 
-            raise AssertionError("Expected not to received any calls, got:\n" +
-                "\n".join([
+            raise AssertionError(
+                "Expected not to received any calls, got:\n" + "\n".join([
                     "call(%s)" % _format_call(c[0], c[1]) for c in calls
                 ])
             )