mirror of
https://mau.dev/maunium/synapse.git
synced 2024-10-01 01:36:05 -04:00
Merge remote-tracking branch 'origin/develop' into test-sqlite-memory
This commit is contained in:
commit
b0406b9ead
23
CHANGES.rst
23
CHANGES.rst
@ -1,3 +1,26 @@
|
||||
Changes in synapse 0.2.3 (2014-09-12)
|
||||
=====================================
|
||||
|
||||
Homeserver:
|
||||
* Fix bug where we stopped sending events to remote home servers if a
|
||||
user from that home server left, even if there were some still in the
|
||||
room.
|
||||
* Fix bugs in the state conflict resolution where it was incorrectly
|
||||
rejecting events.
|
||||
|
||||
Webclient:
|
||||
* Display room names and topics.
|
||||
* Allow setting/editing of room names and topics.
|
||||
* Display information about rooms on the main page.
|
||||
* Handle ban and kick events in real time.
|
||||
* VoIP UI and reliability improvements.
|
||||
* Add glare support for VoIP.
|
||||
* Improvements to initial startup speed.
|
||||
* Don't display duplicate join events.
|
||||
* Local echo of messages.
|
||||
* Differentiate sending and sent of local echo.
|
||||
* Various minor bug fixes.
|
||||
|
||||
Changes in synapse 0.2.2 (2014-09-06)
|
||||
=====================================
|
||||
|
||||
|
@ -5,3 +5,5 @@ Broad-sweeping stuff which would be nice to have
|
||||
- homeserver implementation in go
|
||||
- homeserver implementation in node.js
|
||||
- client SDKs
|
||||
- libpurple library
|
||||
- irssi plugin?
|
||||
|
@ -1182,16 +1182,16 @@ This event is sent by the caller when they wish to establish a call.
|
||||
- ``type`` : "string" - The type of session description, in this case 'offer'
|
||||
- ``sdp`` : "string" - The SDP text of the session description
|
||||
|
||||
``m.call.candidate``
|
||||
``m.call.candidates``
|
||||
This event is sent by callers after sending an invite and by the callee after answering.
|
||||
Its purpose is to give the other party an additional ICE candidate to try using to
|
||||
Its purpose is to give the other party additional ICE candidates to try using to
|
||||
communicate.
|
||||
|
||||
Required keys:
|
||||
- ``call_id`` : "string" - The ID of the call this event relates to
|
||||
- ``version`` : "integer" - The version of the VoIP specification this messages
|
||||
adheres to. his specification is version 0.
|
||||
- ``candidate`` : "candidate object" - Object describing the candidate.
|
||||
- ``candidates`` : "array of candidate objects" - Array of object describing the candidates.
|
||||
|
||||
``Candidate Object``
|
||||
|
||||
|
@ -16,4 +16,4 @@
|
||||
""" This is a reference implementation of a synapse home server.
|
||||
"""
|
||||
|
||||
__version__ = "0.2.2"
|
||||
__version__ = "0.2.3"
|
||||
|
@ -17,6 +17,18 @@ from synapse.api.errors import SynapseError, Codes
|
||||
from synapse.util.jsonobject import JsonEncodedObject
|
||||
|
||||
|
||||
def serialize_event(hs, e):
|
||||
# FIXME(erikj): To handle the case of presence events and the like
|
||||
if not isinstance(e, SynapseEvent):
|
||||
return e
|
||||
|
||||
d = e.get_dict()
|
||||
if "age_ts" in d:
|
||||
d["age"] = int(hs.get_clock().time_msec()) - d["age_ts"]
|
||||
|
||||
return d
|
||||
|
||||
|
||||
class SynapseEvent(JsonEncodedObject):
|
||||
|
||||
"""Base class for Synapse events. These are JSON objects which must abide
|
||||
@ -43,6 +55,7 @@ class SynapseEvent(JsonEncodedObject):
|
||||
"content", # HTTP body, JSON
|
||||
"state_key",
|
||||
"required_power_level",
|
||||
"age_ts",
|
||||
]
|
||||
|
||||
internal_keys = [
|
||||
|
@ -59,6 +59,14 @@ class EventFactory(object):
|
||||
if "ts" not in kwargs:
|
||||
kwargs["ts"] = int(self.clock.time_msec())
|
||||
|
||||
# The "age" key is a delta timestamp that should be converted into an
|
||||
# absolute timestamp the minute we see it.
|
||||
if "age" in kwargs:
|
||||
kwargs["age_ts"] = int(self.clock.time_msec()) - int(kwargs["age"])
|
||||
del kwargs["age"]
|
||||
elif "age_ts" not in kwargs:
|
||||
kwargs["age_ts"] = int(self.clock.time_msec())
|
||||
|
||||
if etype in self._event_list:
|
||||
handler = self._event_list[etype]
|
||||
else:
|
||||
|
@ -291,6 +291,12 @@ class ReplicationLayer(object):
|
||||
def on_incoming_transaction(self, transaction_data):
|
||||
transaction = Transaction(**transaction_data)
|
||||
|
||||
for p in transaction.pdus:
|
||||
if "age" in p:
|
||||
p["age_ts"] = int(self.clock.time_msec()) - int(p["age"])
|
||||
|
||||
pdu_list = [Pdu(**p) for p in transaction.pdus]
|
||||
|
||||
logger.debug("[%s] Got transaction", transaction.transaction_id)
|
||||
|
||||
response = yield self.transaction_actions.have_responded(transaction)
|
||||
@ -303,8 +309,6 @@ class ReplicationLayer(object):
|
||||
|
||||
logger.debug("[%s] Transacition is new", transaction.transaction_id)
|
||||
|
||||
pdu_list = [Pdu(**p) for p in transaction.pdus]
|
||||
|
||||
dl = []
|
||||
for pdu in pdu_list:
|
||||
dl.append(self._handle_new_pdu(pdu))
|
||||
@ -405,9 +409,14 @@ class ReplicationLayer(object):
|
||||
"""Returns a new Transaction containing the given PDUs suitable for
|
||||
transmission.
|
||||
"""
|
||||
pdus = [p.get_dict() for p in pdu_list]
|
||||
for p in pdus:
|
||||
if "age_ts" in pdus:
|
||||
p["age"] = int(self.clock.time_msec()) - p["age_ts"]
|
||||
|
||||
return Transaction(
|
||||
pdus=[p.get_dict() for p in pdu_list],
|
||||
origin=self.server_name,
|
||||
pdus=pdus,
|
||||
ts=int(self._clock.time_msec()),
|
||||
destination=None,
|
||||
)
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
from synapse.api.events import SynapseEvent
|
||||
from synapse.util.logutils import log_function
|
||||
|
||||
from ._base import BaseHandler
|
||||
@ -71,10 +70,7 @@ class EventStreamHandler(BaseHandler):
|
||||
auth_user, room_ids, pagin_config, timeout
|
||||
)
|
||||
|
||||
chunks = [
|
||||
e.get_dict() if isinstance(e, SynapseEvent) else e
|
||||
for e in events
|
||||
]
|
||||
chunks = [self.hs.serialize_event(e) for e in events]
|
||||
|
||||
chunk = {
|
||||
"chunk": chunks,
|
||||
@ -92,7 +88,9 @@ class EventStreamHandler(BaseHandler):
|
||||
# 10 seconds of grace to allow the client to reconnect again
|
||||
# before we think they're gone
|
||||
def _later():
|
||||
logger.debug("_later stopped_user_eventstream %s", auth_user)
|
||||
logger.debug(
|
||||
"_later stopped_user_eventstream %s", auth_user
|
||||
)
|
||||
self.distributor.fire(
|
||||
"stopped_user_eventstream", auth_user
|
||||
)
|
||||
|
@ -124,7 +124,7 @@ class MessageHandler(BaseHandler):
|
||||
)
|
||||
|
||||
chunk = {
|
||||
"chunk": [e.get_dict() for e in events],
|
||||
"chunk": [self.hs.serialize_event(e) for e in events],
|
||||
"start": pagin_config.from_token.to_string(),
|
||||
"end": next_token.to_string(),
|
||||
}
|
||||
@ -296,7 +296,7 @@ class MessageHandler(BaseHandler):
|
||||
end_token = now_token.copy_and_replace("room_key", token[1])
|
||||
|
||||
d["messages"] = {
|
||||
"chunk": [m.get_dict() for m in messages],
|
||||
"chunk": [self.hs.serialize_event(m) for m in messages],
|
||||
"start": start_token.to_string(),
|
||||
"end": end_token.to_string(),
|
||||
}
|
||||
@ -304,7 +304,7 @@ class MessageHandler(BaseHandler):
|
||||
current_state = yield self.store.get_current_state(
|
||||
event.room_id
|
||||
)
|
||||
d["state"] = [c.get_dict() for c in current_state]
|
||||
d["state"] = [self.hs.serialize_event(c) for c in current_state]
|
||||
except:
|
||||
logger.exception("Failed to get snapshot")
|
||||
|
||||
|
@ -335,7 +335,7 @@ class RoomMemberHandler(BaseHandler):
|
||||
|
||||
member_list = yield self.store.get_room_members(room_id=room_id)
|
||||
event_list = [
|
||||
entry.get_dict()
|
||||
self.hs.serialize_event(entry)
|
||||
for entry in member_list
|
||||
]
|
||||
chunk_data = {
|
||||
|
@ -59,7 +59,7 @@ class EventRestServlet(RestServlet):
|
||||
event = yield handler.get_event(auth_user, event_id)
|
||||
|
||||
if event:
|
||||
defer.returnValue((200, event.get_dict()))
|
||||
defer.returnValue((200, self.hs.serialize_event(event)))
|
||||
else:
|
||||
defer.returnValue((404, "Event not found."))
|
||||
|
||||
|
@ -378,7 +378,7 @@ class RoomTriggerBackfill(RestServlet):
|
||||
handler = self.handlers.federation_handler
|
||||
events = yield handler.backfill(remote_server, room_id, limit)
|
||||
|
||||
res = [event.get_dict() for event in events]
|
||||
res = [self.hs.serialize_event(event) for event in events]
|
||||
defer.returnValue((200, res))
|
||||
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
# Imports required for the default HomeServer() implementation
|
||||
from synapse.federation import initialize_http_replication
|
||||
from synapse.api.events import serialize_event
|
||||
from synapse.api.events.factory import EventFactory
|
||||
from synapse.notifier import Notifier
|
||||
from synapse.api.auth import Auth
|
||||
@ -139,6 +140,9 @@ class BaseHomeServer(object):
|
||||
object."""
|
||||
return RoomID.from_string(s, hs=self)
|
||||
|
||||
def serialize_event(self, e):
|
||||
return serialize_event(self, e)
|
||||
|
||||
# Build magic accessors for every dependency
|
||||
for depname in BaseHomeServer.DEPENDENCIES:
|
||||
BaseHomeServer._make_dependency_method(depname)
|
||||
|
@ -36,7 +36,7 @@ from .registration import RegistrationStore
|
||||
from .room import RoomStore
|
||||
from .roommember import RoomMemberStore
|
||||
from .stream import StreamStore
|
||||
from .pdu import StatePduStore, PduStore
|
||||
from .pdu import StatePduStore, PduStore, PdusTable
|
||||
from .transactions import TransactionStore
|
||||
from .keys import KeyStore
|
||||
|
||||
@ -140,6 +140,12 @@ class DataStore(RoomMemberStore, RoomStore,
|
||||
del cols["content"]
|
||||
del cols["prev_pdus"]
|
||||
cols["content_json"] = json.dumps(pdu.content)
|
||||
|
||||
unrec_keys.update({
|
||||
k: v for k, v in cols.items()
|
||||
if k not in PdusTable.fields
|
||||
})
|
||||
|
||||
cols["unrecognized_keys"] = json.dumps(unrec_keys)
|
||||
|
||||
logger.debug("Persisting: %s", repr(cols))
|
||||
|
@ -355,6 +355,10 @@ class SQLBaseStore(object):
|
||||
d["content"] = json.loads(d["content"])
|
||||
del d["unrecognized_keys"]
|
||||
|
||||
if "age_ts" not in d:
|
||||
# For compatibility
|
||||
d["age_ts"] = d["ts"] if "ts" in d else 0
|
||||
|
||||
return self.event_factory.create_event(
|
||||
etype=d["type"],
|
||||
**d
|
||||
|
@ -66,8 +66,8 @@ class RoomMemberStore(SQLBaseStore):
|
||||
# Check if this was the last person to have left.
|
||||
member_events = self._get_members_query_txn(
|
||||
txn,
|
||||
where_clause="c.room_id = ? AND m.membership = ?",
|
||||
where_values=(event.room_id, Membership.JOIN,)
|
||||
where_clause="c.room_id = ? AND m.membership = ? AND m.user_id != ?",
|
||||
where_values=(event.room_id, Membership.JOIN, target_user_id,)
|
||||
)
|
||||
|
||||
joined_domains = set()
|
||||
|
@ -1,6 +1,6 @@
|
||||
from synapse.api.ratelimiting import Ratelimiter
|
||||
|
||||
import unittest
|
||||
from tests import unittest
|
||||
|
||||
class TestRatelimiter(unittest.TestCase):
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
from synapse.api.events import SynapseEvent
|
||||
|
||||
import unittest
|
||||
from tests import unittest
|
||||
|
||||
|
||||
class SynapseTemplateCheckTestCase(unittest.TestCase):
|
||||
|
@ -14,11 +14,10 @@
|
||||
|
||||
# trial imports
|
||||
from twisted.internet import defer
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
|
||||
# python imports
|
||||
from mock import Mock
|
||||
import logging
|
||||
|
||||
from ..utils import MockHttpResource, MockClock
|
||||
|
||||
@ -28,9 +27,6 @@ from synapse.federation.units import Pdu
|
||||
from synapse.storage.pdu import PduTuple, PduEntry
|
||||
|
||||
|
||||
logging.getLogger().addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
def make_pdu(prev_pdus=[], **kwargs):
|
||||
"""Provide some default fields for making a PduTuple."""
|
||||
pdu_fields = {
|
||||
|
@ -13,7 +13,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
|
||||
from synapse.federation.pdu_codec import (
|
||||
PduCodec, encode_event_id, decode_event_id
|
||||
|
@ -14,11 +14,10 @@
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
from twisted.internet import defer
|
||||
|
||||
from mock import Mock
|
||||
import logging
|
||||
|
||||
from synapse.server import HomeServer
|
||||
from synapse.http.client import HttpClient
|
||||
@ -28,9 +27,6 @@ from synapse.storage.directory import RoomAliasMapping
|
||||
from tests.utils import SQLiteMemoryDbPool
|
||||
|
||||
|
||||
logging.getLogger().addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
class DirectoryHandlers(object):
|
||||
def __init__(self, hs):
|
||||
self.directory_handler = DirectoryHandler(hs)
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
|
||||
from twisted.internet import defer
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
|
||||
from synapse.api.events.room import (
|
||||
InviteJoinEvent, MessageEvent, RoomMemberEvent
|
||||
@ -26,12 +26,8 @@ from synapse.federation.units import Pdu
|
||||
|
||||
from mock import NonCallableMock, ANY
|
||||
|
||||
import logging
|
||||
|
||||
from ..utils import get_mock_call_args
|
||||
|
||||
logging.getLogger().addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
class FederationTestCase(unittest.TestCase):
|
||||
|
||||
|
@ -14,11 +14,10 @@
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
from twisted.internet import defer, reactor
|
||||
|
||||
from mock import Mock, call, ANY
|
||||
import logging
|
||||
import json
|
||||
|
||||
from tests.utils import (
|
||||
@ -36,9 +35,6 @@ UNAVAILABLE = PresenceState.UNAVAILABLE
|
||||
ONLINE = PresenceState.ONLINE
|
||||
|
||||
|
||||
logging.getLogger().addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
def _expect_edu(destination, edu_type, content, origin="test"):
|
||||
return {
|
||||
"origin": origin,
|
||||
@ -85,7 +81,6 @@ class PresenceStateTestCase(unittest.TestCase):
|
||||
# Mock the RoomMemberHandler
|
||||
room_member_handler = Mock(spec=[])
|
||||
hs.handlers.room_member_handler = room_member_handler
|
||||
logging.getLogger().debug("Mocking room_member_handler=%r", room_member_handler)
|
||||
|
||||
# Some local users to test with
|
||||
self.u_apple = hs.parse_userid("@apple:test")
|
||||
|
@ -16,11 +16,10 @@
|
||||
"""This file contains tests of the "presence-like" data that is shared between
|
||||
presence and profiles; namely, the displayname and avatar_url."""
|
||||
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
from twisted.internet import defer
|
||||
|
||||
from mock import Mock, call, ANY
|
||||
import logging
|
||||
|
||||
from ..utils import MockClock
|
||||
|
||||
@ -35,9 +34,6 @@ UNAVAILABLE = PresenceState.UNAVAILABLE
|
||||
ONLINE = PresenceState.ONLINE
|
||||
|
||||
|
||||
logging.getLogger().addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
class MockReplication(object):
|
||||
def __init__(self):
|
||||
self.edu_handlers = {}
|
||||
|
@ -14,11 +14,10 @@
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
from twisted.internet import defer
|
||||
|
||||
from mock import Mock
|
||||
import logging
|
||||
|
||||
from synapse.api.errors import AuthError
|
||||
from synapse.server import HomeServer
|
||||
@ -27,9 +26,6 @@ from synapse.handlers.profile import ProfileHandler
|
||||
from tests.utils import SQLiteMemoryDbPool
|
||||
|
||||
|
||||
logging.getLogger().addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
class ProfileHandlers(object):
|
||||
def __init__(self, hs):
|
||||
self.profile_handler = ProfileHandler(hs)
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
|
||||
from twisted.internet import defer
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
|
||||
from synapse.api.events.room import (
|
||||
InviteJoinEvent, RoomMemberEvent, RoomConfigEvent
|
||||
@ -27,10 +27,6 @@ from synapse.server import HomeServer
|
||||
|
||||
from mock import Mock, NonCallableMock
|
||||
|
||||
import logging
|
||||
|
||||
logging.getLogger().addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
class RoomMemberHandlerTestCase(unittest.TestCase):
|
||||
|
||||
|
@ -14,12 +14,11 @@
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
from twisted.internet import defer
|
||||
|
||||
from mock import Mock, call, ANY
|
||||
import json
|
||||
import logging
|
||||
|
||||
from ..utils import MockHttpResource, MockClock, DeferredMockCallable
|
||||
|
||||
@ -27,9 +26,6 @@ from synapse.server import HomeServer
|
||||
from synapse.handlers.typing import TypingNotificationHandler
|
||||
|
||||
|
||||
logging.getLogger().addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
def _expect_edu(destination, edu_type, content, origin="test"):
|
||||
return {
|
||||
"origin": origin,
|
||||
|
@ -14,7 +14,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
""" Tests REST events for /events paths."""
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
|
||||
# twisted imports
|
||||
from twisted.internet import defer
|
||||
@ -27,14 +27,12 @@ from synapse.server import HomeServer
|
||||
|
||||
# python imports
|
||||
import json
|
||||
import logging
|
||||
|
||||
from ..utils import MockHttpResource, MemoryDataStore
|
||||
from .utils import RestTestCase
|
||||
|
||||
from mock import Mock, NonCallableMock
|
||||
|
||||
logging.getLogger().addHandler(logging.NullHandler())
|
||||
|
||||
PATH_PREFIX = "/_matrix/client/api/v1"
|
||||
|
||||
|
@ -15,11 +15,10 @@
|
||||
|
||||
"""Tests REST events for /presence paths."""
|
||||
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
from twisted.internet import defer
|
||||
|
||||
from mock import Mock
|
||||
import logging
|
||||
|
||||
from ..utils import MockHttpResource
|
||||
|
||||
@ -28,9 +27,6 @@ from synapse.handlers.presence import PresenceHandler
|
||||
from synapse.server import HomeServer
|
||||
|
||||
|
||||
logging.getLogger().addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
OFFLINE = PresenceState.OFFLINE
|
||||
UNAVAILABLE = PresenceState.UNAVAILABLE
|
||||
ONLINE = PresenceState.ONLINE
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
"""Tests REST events for /profile paths."""
|
||||
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
from twisted.internet import defer
|
||||
|
||||
from mock import Mock
|
||||
@ -28,6 +28,7 @@ from synapse.server import HomeServer
|
||||
myid = "@1234ABCD:test"
|
||||
PATH_PREFIX = "/_matrix/client/api/v1"
|
||||
|
||||
|
||||
class ProfileTestCase(unittest.TestCase):
|
||||
""" Tests profile management. """
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
from twisted.internet import defer
|
||||
|
||||
# trial imports
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
|
||||
from synapse.api.constants import Membership
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
from twisted.internet import defer
|
||||
|
||||
from mock import Mock, call
|
||||
|
@ -13,8 +13,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from tests import unittest
|
||||
from twisted.internet import defer
|
||||
from twisted.trial import unittest
|
||||
|
||||
from mock import Mock, patch
|
||||
|
||||
|
@ -13,8 +13,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from tests import unittest
|
||||
from twisted.internet import defer
|
||||
from twisted.trial import unittest
|
||||
from twisted.python.log import PythonLoggingObserver
|
||||
|
||||
from synapse.state import StateHandler
|
||||
@ -26,7 +26,6 @@ from collections import namedtuple
|
||||
|
||||
from mock import Mock
|
||||
|
||||
import logging
|
||||
import mock
|
||||
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import unittest
|
||||
from tests import unittest
|
||||
|
||||
from synapse.server import BaseHomeServer
|
||||
from synapse.types import UserID, RoomAlias
|
||||
|
79
tests/unittest.py
Normal file
79
tests/unittest.py
Normal file
@ -0,0 +1,79 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2014 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 twisted.trial import unittest
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
# logging doesn't have a "don't log anything at all EVARRRR setting,
|
||||
# but since the highest value is 50, 1000000 should do ;)
|
||||
NEVER = 1000000
|
||||
|
||||
logging.getLogger().addHandler(logging.StreamHandler())
|
||||
logging.getLogger().setLevel(NEVER)
|
||||
|
||||
|
||||
def around(target):
|
||||
"""A CLOS-style 'around' modifier, which wraps the original method of the
|
||||
given instance with another piece of code.
|
||||
|
||||
@around(self)
|
||||
def method_name(orig, *args, **kwargs):
|
||||
return orig(*args, **kwargs)
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
class TestCase(unittest.TestCase):
|
||||
"""A subclass of twisted.trial's TestCase which looks for 'loglevel'
|
||||
attributes on both itself and its individual test methods, to override the
|
||||
root logger's logging level while that test (case|method) runs."""
|
||||
|
||||
def __init__(self, methodName, *args, **kwargs):
|
||||
super(TestCase, self).__init__(methodName, *args, **kwargs)
|
||||
|
||||
method = getattr(self, methodName)
|
||||
|
||||
level = getattr(method, "loglevel",
|
||||
getattr(self, "loglevel",
|
||||
NEVER))
|
||||
|
||||
@around(self)
|
||||
def setUp(orig):
|
||||
old_level = logging.getLogger().level
|
||||
|
||||
if old_level != level:
|
||||
@around(self)
|
||||
def tearDown(orig):
|
||||
ret = orig()
|
||||
logging.getLogger().setLevel(old_level)
|
||||
return ret
|
||||
|
||||
logging.getLogger().setLevel(level)
|
||||
return orig()
|
||||
|
||||
|
||||
def DEBUG(target):
|
||||
"""A decorator to set the .loglevel attribute to logging.DEBUG.
|
||||
Can apply to either a TestCase or an individual test method."""
|
||||
target.loglevel = logging.DEBUG
|
||||
return target
|
@ -15,7 +15,7 @@
|
||||
|
||||
|
||||
from twisted.internet import defer
|
||||
from twisted.trial import unittest
|
||||
from tests import unittest
|
||||
|
||||
from synapse.util.lockutils import LockManager
|
||||
|
||||
|
@ -528,8 +528,8 @@ a:active { color: #000; }
|
||||
}
|
||||
|
||||
.bubble .message {
|
||||
/* Break lines when encountering CR+LF */
|
||||
white-space: pre;
|
||||
/* Wrap words and break lines on CR+LF */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.bubble .messagePending {
|
||||
opacity: 0.3
|
||||
|
@ -38,6 +38,13 @@ angular.module('eventHandlerService', [])
|
||||
var TOPIC_EVENT = "TOPIC_EVENT";
|
||||
var RESET_EVENT = "RESET_EVENT"; // eventHandlerService has been resetted
|
||||
|
||||
// used for dedupping events - could be expanded in future...
|
||||
// FIXME: means that we leak memory over time (along with lots of the rest
|
||||
// of the app, given we never try to reap memory yet)
|
||||
var eventMap = {};
|
||||
|
||||
$rootScope.presence = {};
|
||||
|
||||
var initialSyncDeferred;
|
||||
|
||||
var reset = function() {
|
||||
@ -46,16 +53,13 @@ angular.module('eventHandlerService', [])
|
||||
$rootScope.events = {
|
||||
rooms: {} // will contain roomId: { messages:[], members:{userid1: event} }
|
||||
};
|
||||
}
|
||||
reset();
|
||||
|
||||
// used for dedupping events - could be expanded in future...
|
||||
// FIXME: means that we leak memory over time (along with lots of the rest
|
||||
// of the app, given we never try to reap memory yet)
|
||||
var eventMap = {};
|
||||
|
||||
$rootScope.presence = {};
|
||||
|
||||
eventMap = {};
|
||||
};
|
||||
reset();
|
||||
|
||||
var initRoom = function(room_id) {
|
||||
if (!(room_id in $rootScope.events.rooms)) {
|
||||
console.log("Creating new handler entry for " + room_id);
|
||||
@ -204,7 +208,7 @@ angular.module('eventHandlerService', [])
|
||||
|
||||
var handleCallEvent = function(event, isLiveEvent) {
|
||||
$rootScope.$broadcast(CALL_EVENT, event, isLiveEvent);
|
||||
if (event.type == 'm.call.invite') {
|
||||
if (event.type === 'm.call.invite') {
|
||||
$rootScope.events.rooms[event.room_id].messages.push(event);
|
||||
}
|
||||
};
|
||||
@ -231,7 +235,7 @@ angular.module('eventHandlerService', [])
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
ROOM_CREATE_EVENT: ROOM_CREATE_EVENT,
|
||||
|
@ -47,6 +47,10 @@ angular.module('MatrixCall', [])
|
||||
this.call_id = "c" + new Date().getTime();
|
||||
this.state = 'fledgling';
|
||||
this.didConnect = false;
|
||||
|
||||
// a queue for candidates waiting to go out. We try to amalgamate candidates into a single candidate message where possible
|
||||
this.candidateSendQueue = [];
|
||||
this.candidateSendTries = 0;
|
||||
}
|
||||
|
||||
MatrixCall.prototype.createPeerConnection = function() {
|
||||
@ -174,12 +178,7 @@ angular.module('MatrixCall', [])
|
||||
MatrixCall.prototype.gotLocalIceCandidate = function(event) {
|
||||
console.log(event);
|
||||
if (event.candidate) {
|
||||
var content = {
|
||||
version: 0,
|
||||
call_id: this.call_id,
|
||||
candidate: event.candidate
|
||||
};
|
||||
this.sendEventWithRetry('m.call.candidate', content);
|
||||
this.sendCandidate(event.candidate);
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,5 +369,53 @@ angular.module('MatrixCall', [])
|
||||
}, delayMs);
|
||||
};
|
||||
|
||||
// Sends candidates with are sent in a special way because we try to amalgamate them into one message
|
||||
MatrixCall.prototype.sendCandidate = function(content) {
|
||||
this.candidateSendQueue.push(content);
|
||||
var self = this;
|
||||
if (this.candidateSendTries == 0) $timeout(function() { self.sendCandidateQueue(); }, 100);
|
||||
};
|
||||
|
||||
MatrixCall.prototype.sendCandidateQueue = function(content) {
|
||||
if (this.candidateSendQueue.length == 0) return;
|
||||
|
||||
var cands = this.candidateSendQueue;
|
||||
this.candidateSendQueue = [];
|
||||
++this.candidateSendTries;
|
||||
var content = {
|
||||
version: 0,
|
||||
call_id: this.call_id,
|
||||
candidates: cands
|
||||
};
|
||||
var self = this;
|
||||
console.log("Attempting to send "+cands.length+" candidates");
|
||||
matrixService.sendEvent(self.room_id, 'm.call.candidates', undefined, content).then(function() { self.candsSent(); }, function(error) { self.candsSendFailed(cands, error); } );
|
||||
};
|
||||
|
||||
MatrixCall.prototype.candsSent = function() {
|
||||
this.candidateSendTries = 0;
|
||||
this.sendCandidateQueue();
|
||||
};
|
||||
|
||||
MatrixCall.prototype.candsSendFailed = function(cands, error) {
|
||||
for (var i = 0; i < cands.length; ++i) {
|
||||
this.candidateSendQueue.push(cands[i]);
|
||||
}
|
||||
|
||||
if (this.candidateSendTries > 5) {
|
||||
console.log("Failed to send candidates on attempt "+ev.tries+". Giving up for now.");
|
||||
this.candidateSendTries = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var delayMs = 500 * Math.pow(2, this.candidateSendTries);
|
||||
++this.candidateSendTries;
|
||||
console.log("Failed to send candidates. Retrying in "+delayMs+"ms");
|
||||
var self = this;
|
||||
$timeout(function() {
|
||||
self.sendCandidateQueue();
|
||||
}, delayMs);
|
||||
};
|
||||
|
||||
return MatrixCall;
|
||||
}]);
|
||||
|
@ -77,13 +77,15 @@ angular.module('matrixPhoneService', [])
|
||||
return;
|
||||
}
|
||||
call.receivedAnswer(msg);
|
||||
} else if (event.type == 'm.call.candidate') {
|
||||
} else if (event.type == 'm.call.candidates') {
|
||||
var call = matrixPhoneService.allCalls[msg.call_id];
|
||||
if (!call) {
|
||||
console.log("Got candidate for unknown call ID "+msg.call_id);
|
||||
console.log("Got candidates for unknown call ID "+msg.call_id);
|
||||
return;
|
||||
}
|
||||
call.gotRemoteIceCandidate(msg.candidate);
|
||||
for (var i = 0; i < msg.candidates.length; ++i) {
|
||||
call.gotRemoteIceCandidate(msg.candidates[i]);
|
||||
}
|
||||
} else if (event.type == 'm.call.hangup') {
|
||||
var call = matrixPhoneService.allCalls[msg.call_id];
|
||||
if (!call) {
|
||||
|
@ -22,7 +22,7 @@
|
||||
<td colspan="3" class="recentsRoomSummary">
|
||||
|
||||
<div ng-show="room.membership === 'invite'">
|
||||
{{ room.lastMsg.inviter | mUserDisplayName: room.room_id }} invited you
|
||||
{{ room.inviter | mUserDisplayName: room.room_id }} invited you
|
||||
</div>
|
||||
|
||||
<div ng-hide="room.membership === 'invite'" ng-switch="room.lastMsg.type">
|
||||
|
@ -220,7 +220,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
||||
};
|
||||
|
||||
var paginate = function(numItems) {
|
||||
// console.log("paginate " + numItems);
|
||||
//console.log("paginate " + numItems + " and first_pagination is " + $scope.state.first_pagination);
|
||||
if ($scope.state.paginating || !$scope.room_id) {
|
||||
return;
|
||||
}
|
||||
@ -260,7 +260,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
||||
}
|
||||
|
||||
if ($scope.state.first_pagination) {
|
||||
scrollToBottom();
|
||||
scrollToBottom(true);
|
||||
$scope.state.first_pagination = false;
|
||||
}
|
||||
else {
|
||||
@ -598,6 +598,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
||||
promise.then(
|
||||
function(response) {
|
||||
console.log("Request successfully sent");
|
||||
|
||||
if (echo) {
|
||||
// Mark this fake message event with its allocated event_id
|
||||
// When the true message event will come from the events stream (in handleMessage),
|
||||
|
Loading…
Reference in New Issue
Block a user