mirror of
https://github.com/element-hq/dendrite.git
synced 2025-09-14 05:12:26 +03:00
Per-user-per-device sync streams (#1068)
* Per-user-per-device sync streams * Tweaks * Tweaks * Pass full device into CompleteSync * Set user IDs and device IDs properly in tests * Add new test, fix TestNewEventAndWasPreviouslyJoinedToRoom * nolint a function that is not used yet * Add test for waking up single device * Hopefully unstick test * Try to ensure that TestCorrectStreamWakeup doesn't block forever * Update tests
This commit is contained in:
parent
57841fc35e
commit
02fe38e1f7
8 changed files with 161 additions and 70 deletions
|
@ -41,9 +41,11 @@ var (
|
|||
)
|
||||
|
||||
var (
|
||||
roomID = "!test:localhost"
|
||||
alice = "@alice:localhost"
|
||||
bob = "@bob:localhost"
|
||||
roomID = "!test:localhost"
|
||||
alice = "@alice:localhost"
|
||||
aliceDev = "alicedevice"
|
||||
bob = "@bob:localhost"
|
||||
bobDev = "bobdev"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -107,7 +109,7 @@ func mustEqualPositions(t *testing.T, got, want types.StreamingToken) {
|
|||
// Test that the current position is returned if a request is already behind.
|
||||
func TestImmediateNotification(t *testing.T) {
|
||||
n := NewNotifier(syncPositionBefore)
|
||||
pos, err := waitForEvents(n, newTestSyncRequest(alice, syncPositionVeryOld))
|
||||
pos, err := waitForEvents(n, newTestSyncRequest(alice, aliceDev, syncPositionVeryOld))
|
||||
if err != nil {
|
||||
t.Fatalf("TestImmediateNotification error: %s", err)
|
||||
}
|
||||
|
@ -124,7 +126,7 @@ func TestNewEventAndJoinedToRoom(t *testing.T) {
|
|||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
pos, err := waitForEvents(n, newTestSyncRequest(bob, syncPositionBefore))
|
||||
pos, err := waitForEvents(n, newTestSyncRequest(bob, bobDev, syncPositionBefore))
|
||||
if err != nil {
|
||||
t.Errorf("TestNewEventAndJoinedToRoom error: %w", err)
|
||||
}
|
||||
|
@ -132,7 +134,7 @@ func TestNewEventAndJoinedToRoom(t *testing.T) {
|
|||
wg.Done()
|
||||
}()
|
||||
|
||||
stream := lockedFetchUserStream(n, bob)
|
||||
stream := lockedFetchUserStream(n, bob, bobDev)
|
||||
waitForBlocking(stream, 1)
|
||||
|
||||
n.OnNewEvent(&randomMessageEvent, "", nil, syncPositionAfter)
|
||||
|
@ -140,6 +142,43 @@ func TestNewEventAndJoinedToRoom(t *testing.T) {
|
|||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestCorrectStream(t *testing.T) {
|
||||
n := NewNotifier(syncPositionBefore)
|
||||
stream := lockedFetchUserStream(n, bob, bobDev)
|
||||
if stream.UserID != bob {
|
||||
t.Fatalf("expected user %q, got %q", bob, stream.UserID)
|
||||
}
|
||||
if stream.DeviceID != bobDev {
|
||||
t.Fatalf("expected device %q, got %q", bobDev, stream.DeviceID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCorrectStreamWakeup(t *testing.T) {
|
||||
n := NewNotifier(syncPositionBefore)
|
||||
awoken := make(chan string)
|
||||
|
||||
streamone := lockedFetchUserStream(n, alice, "one")
|
||||
streamtwo := lockedFetchUserStream(n, alice, "two")
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-streamone.signalChannel:
|
||||
awoken <- "one"
|
||||
case <-streamtwo.signalChannel:
|
||||
awoken <- "two"
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
wake := "two"
|
||||
n.wakeupUserDevice(map[string]string{alice: wake}, syncPositionAfter)
|
||||
|
||||
if result := <-awoken; result != wake {
|
||||
t.Fatalf("expected to wake %q, got %q", wake, result)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that an invite unblocks the request
|
||||
func TestNewInviteEventForUser(t *testing.T) {
|
||||
n := NewNotifier(syncPositionBefore)
|
||||
|
@ -150,7 +189,7 @@ func TestNewInviteEventForUser(t *testing.T) {
|
|||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
pos, err := waitForEvents(n, newTestSyncRequest(bob, syncPositionBefore))
|
||||
pos, err := waitForEvents(n, newTestSyncRequest(bob, bobDev, syncPositionBefore))
|
||||
if err != nil {
|
||||
t.Errorf("TestNewInviteEventForUser error: %w", err)
|
||||
}
|
||||
|
@ -158,7 +197,7 @@ func TestNewInviteEventForUser(t *testing.T) {
|
|||
wg.Done()
|
||||
}()
|
||||
|
||||
stream := lockedFetchUserStream(n, bob)
|
||||
stream := lockedFetchUserStream(n, bob, bobDev)
|
||||
waitForBlocking(stream, 1)
|
||||
|
||||
n.OnNewEvent(&aliceInviteBobEvent, "", nil, syncPositionAfter)
|
||||
|
@ -176,7 +215,7 @@ func TestEDUWakeup(t *testing.T) {
|
|||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
pos, err := waitForEvents(n, newTestSyncRequest(bob, syncPositionAfter))
|
||||
pos, err := waitForEvents(n, newTestSyncRequest(bob, bobDev, syncPositionAfter))
|
||||
if err != nil {
|
||||
t.Errorf("TestNewInviteEventForUser error: %w", err)
|
||||
}
|
||||
|
@ -184,7 +223,7 @@ func TestEDUWakeup(t *testing.T) {
|
|||
wg.Done()
|
||||
}()
|
||||
|
||||
stream := lockedFetchUserStream(n, bob)
|
||||
stream := lockedFetchUserStream(n, bob, bobDev)
|
||||
waitForBlocking(stream, 1)
|
||||
|
||||
n.OnNewEvent(&aliceInviteBobEvent, "", nil, syncPositionNewEDU)
|
||||
|
@ -202,7 +241,7 @@ func TestMultipleRequestWakeup(t *testing.T) {
|
|||
var wg sync.WaitGroup
|
||||
wg.Add(3)
|
||||
poll := func() {
|
||||
pos, err := waitForEvents(n, newTestSyncRequest(bob, syncPositionBefore))
|
||||
pos, err := waitForEvents(n, newTestSyncRequest(bob, bobDev, syncPositionBefore))
|
||||
if err != nil {
|
||||
t.Errorf("TestMultipleRequestWakeup error: %w", err)
|
||||
}
|
||||
|
@ -213,7 +252,7 @@ func TestMultipleRequestWakeup(t *testing.T) {
|
|||
go poll()
|
||||
go poll()
|
||||
|
||||
stream := lockedFetchUserStream(n, bob)
|
||||
stream := lockedFetchUserStream(n, bob, bobDev)
|
||||
waitForBlocking(stream, 3)
|
||||
|
||||
n.OnNewEvent(&randomMessageEvent, "", nil, syncPositionAfter)
|
||||
|
@ -240,24 +279,24 @@ func TestNewEventAndWasPreviouslyJoinedToRoom(t *testing.T) {
|
|||
// Make bob leave the room
|
||||
leaveWG.Add(1)
|
||||
go func() {
|
||||
pos, err := waitForEvents(n, newTestSyncRequest(bob, syncPositionBefore))
|
||||
pos, err := waitForEvents(n, newTestSyncRequest(bob, bobDev, syncPositionBefore))
|
||||
if err != nil {
|
||||
t.Errorf("TestNewEventAndWasPreviouslyJoinedToRoom error: %w", err)
|
||||
}
|
||||
mustEqualPositions(t, pos, syncPositionAfter)
|
||||
leaveWG.Done()
|
||||
}()
|
||||
bobStream := lockedFetchUserStream(n, bob)
|
||||
bobStream := lockedFetchUserStream(n, bob, bobDev)
|
||||
waitForBlocking(bobStream, 1)
|
||||
n.OnNewEvent(&bobLeaveEvent, "", nil, syncPositionAfter)
|
||||
leaveWG.Wait()
|
||||
|
||||
// send an event into the room. Make sure alice gets it. Bob should not.
|
||||
var aliceWG sync.WaitGroup
|
||||
aliceStream := lockedFetchUserStream(n, alice)
|
||||
aliceStream := lockedFetchUserStream(n, alice, aliceDev)
|
||||
aliceWG.Add(1)
|
||||
go func() {
|
||||
pos, err := waitForEvents(n, newTestSyncRequest(alice, syncPositionAfter))
|
||||
pos, err := waitForEvents(n, newTestSyncRequest(alice, aliceDev, syncPositionAfter))
|
||||
if err != nil {
|
||||
t.Errorf("TestNewEventAndWasPreviouslyJoinedToRoom error: %w", err)
|
||||
}
|
||||
|
@ -267,7 +306,7 @@ func TestNewEventAndWasPreviouslyJoinedToRoom(t *testing.T) {
|
|||
|
||||
go func() {
|
||||
// this should timeout with an error (but the main goroutine won't wait for the timeout explicitly)
|
||||
_, err := waitForEvents(n, newTestSyncRequest(bob, syncPositionAfter))
|
||||
_, err := waitForEvents(n, newTestSyncRequest(bob, bobDev, syncPositionAfter))
|
||||
if err == nil {
|
||||
t.Errorf("TestNewEventAndWasPreviouslyJoinedToRoom expect error but got nil")
|
||||
}
|
||||
|
@ -300,7 +339,7 @@ func waitForEvents(n *Notifier, req syncRequest) (types.StreamingToken, error) {
|
|||
}
|
||||
|
||||
// Wait until something is Wait()ing on the user stream.
|
||||
func waitForBlocking(s *UserStream, numBlocking uint) {
|
||||
func waitForBlocking(s *UserDeviceStream, numBlocking uint) {
|
||||
for numBlocking != s.NumWaiting() {
|
||||
// This is horrible but I don't want to add a signalling mechanism JUST for testing.
|
||||
time.Sleep(1 * time.Microsecond)
|
||||
|
@ -309,16 +348,19 @@ func waitForBlocking(s *UserStream, numBlocking uint) {
|
|||
|
||||
// lockedFetchUserStream invokes Notifier.fetchUserStream, respecting Notifier.streamLock.
|
||||
// A new stream is made if it doesn't exist already.
|
||||
func lockedFetchUserStream(n *Notifier, userID string) *UserStream {
|
||||
func lockedFetchUserStream(n *Notifier, userID, deviceID string) *UserDeviceStream {
|
||||
n.streamLock.Lock()
|
||||
defer n.streamLock.Unlock()
|
||||
|
||||
return n.fetchUserStream(userID, true)
|
||||
return n.fetchUserDeviceStream(userID, deviceID, true)
|
||||
}
|
||||
|
||||
func newTestSyncRequest(userID string, since types.StreamingToken) syncRequest {
|
||||
func newTestSyncRequest(userID, deviceID string, since types.StreamingToken) syncRequest {
|
||||
return syncRequest{
|
||||
device: authtypes.Device{UserID: userID},
|
||||
device: authtypes.Device{
|
||||
UserID: userID,
|
||||
ID: deviceID,
|
||||
},
|
||||
timeout: 1 * time.Minute,
|
||||
since: &since,
|
||||
wantFullState: false,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue