-
Notifications
You must be signed in to change notification settings - Fork 354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(plugin-meetings): stop stats analyzer during reconnection #3990
Changes from all commits
757cd07
23bf8cf
457fcd4
27a3b93
178e655
4afdb71
ceb7fd0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -377,7 +377,10 @@ describe('plugin-meetings', () => { | |
} | ||
); | ||
assert.equal(newMeeting.correlationId, newMeeting.id); | ||
assert.deepEqual(newMeeting.callStateForMetrics, {correlationId: newMeeting.id, sessionCorrelationId: ''}); | ||
assert.deepEqual(newMeeting.callStateForMetrics, { | ||
correlationId: newMeeting.id, | ||
sessionCorrelationId: '', | ||
}); | ||
}); | ||
|
||
it('correlationId can be provided in callStateForMetrics', () => { | ||
|
@@ -3808,12 +3811,12 @@ describe('plugin-meetings', () => { | |
id: 'fake locus from mocked join request', | ||
locusUrl: 'fake locus url', | ||
mediaId: 'fake media id', | ||
}) | ||
}); | ||
sinon.stub(meeting.meetingRequest, 'joinMeeting').resolves({ | ||
headers: { | ||
trackingid: 'fake tracking id', | ||
} | ||
}) | ||
}, | ||
}); | ||
await meeting.join({enableMultistream: isMultistream}); | ||
}); | ||
|
||
|
@@ -4006,7 +4009,10 @@ describe('plugin-meetings', () => { | |
assert.notCalled( | ||
meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream | ||
); | ||
assert.throws(meeting.publishStreams(localStreams), `Attempted to publish microphone stream with ended readyState, correlationId=${meeting.correlationId}`); | ||
assert.throws( | ||
meeting.publishStreams(localStreams), | ||
`Attempted to publish microphone stream with ended readyState, correlationId=${meeting.correlationId}` | ||
); | ||
Comment on lines
+4012
to
+4015
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Duplicate error handling code for stream readyState checks Similar error handling code is duplicated for checking stream readyState across audio, video, screenShare audio and screenShare video streams. This should be consolidated into a reusable helper function. // Add helper function:
+ const validateStreamReadyState = (stream, type) => {
+ if (stream?.readyState === 'ended') {
+ throw new Error(`Attempted to publish ${type} stream with ended readyState, correlationId=${meeting.correlationId}`);
+ }
+ }
// Use helper function:
- if (stream?.readyState === 'ended') {
- assert.throws(
- meeting.publishStreams(localStreams),
- `Attempted to publish microphone stream with ended readyState, correlationId=${meeting.correlationId}`
- );
- }
+ validateStreamReadyState(stream, 'microphone'); Also applies to: 4028-4031, 4044-4047, 4060-4063 |
||
} else { | ||
assert.calledOnceWithExactly( | ||
meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream, | ||
|
@@ -4019,7 +4025,10 @@ describe('plugin-meetings', () => { | |
assert.notCalled( | ||
meeting.sendSlotManager.getSlot(MediaType.VideoMain).publishStream | ||
); | ||
assert.throws(meeting.publishStreams(localStreams), `Attempted to publish camera stream with ended readyState, correlationId=${meeting.correlationId}`); | ||
assert.throws( | ||
meeting.publishStreams(localStreams), | ||
`Attempted to publish camera stream with ended readyState, correlationId=${meeting.correlationId}` | ||
); | ||
} else { | ||
assert.calledOnceWithExactly( | ||
meeting.sendSlotManager.getSlot(MediaType.VideoMain).publishStream, | ||
|
@@ -4032,7 +4041,10 @@ describe('plugin-meetings', () => { | |
assert.notCalled( | ||
meeting.sendSlotManager.getSlot(MediaType.AudioSlides).publishStream | ||
); | ||
assert.throws(meeting.publishStreams(localStreams), `Attempted to publish screenShare audio stream with ended readyState, correlationId=${meeting.correlationId}`); | ||
assert.throws( | ||
meeting.publishStreams(localStreams), | ||
`Attempted to publish screenShare audio stream with ended readyState, correlationId=${meeting.correlationId}` | ||
); | ||
Comment on lines
+4044
to
+4047
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Refactor duplicate error handling code for screen share streams Similar error handling code is duplicated for both screenShare audio and video streams. This should be consolidated with the earlier microphone stream error handling into a single reusable function. // Use the same helper function:
const handleStreamError = (streamType, correlationId) => {
throw new Error(`Attempted to publish ${streamType} stream with ended readyState, correlationId=${correlationId}`);
};
// Then use it consistently:
if (stream?.readyState === 'ended') {
handleStreamError('screenShare audio', meeting.correlationId);
}
if (stream?.readyState === 'ended') {
handleStreamError('screenShare video', meeting.correlationId);
} Also applies to: 4047-4050 |
||
} else { | ||
assert.calledOnceWithExactly( | ||
meeting.sendSlotManager.getSlot(MediaType.AudioSlides).publishStream, | ||
|
@@ -4045,7 +4057,10 @@ describe('plugin-meetings', () => { | |
assert.notCalled( | ||
meeting.sendSlotManager.getSlot(MediaType.VideoSlides).publishStream | ||
); | ||
assert.throws(meeting.publishStreams(localStreams), `Attempted to publish screenShare video stream with ended readyState, correlationId=${meeting.correlationId}`); | ||
assert.throws( | ||
meeting.publishStreams(localStreams), | ||
`Attempted to publish screenShare video stream with ended readyState, correlationId=${meeting.correlationId}` | ||
); | ||
} else { | ||
assert.calledOnceWithExactly( | ||
meeting.sendSlotManager.getSlot(MediaType.VideoSlides).publishStream, | ||
|
@@ -4340,14 +4355,14 @@ describe('plugin-meetings', () => { | |
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging'); | ||
await meeting.addMedia({audioEnabled: false}); | ||
//calling handleDeviceLogging with audioEnaled as true adn videoEnabled as false | ||
assert.calledWith(handleDeviceLoggingSpy,false,true); | ||
assert.calledWith(handleDeviceLoggingSpy, false, true); | ||
}); | ||
|
||
it('addMedia() works correctly when video is disabled with no streams to publish', async () => { | ||
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging'); | ||
await meeting.addMedia({videoEnabled: false}); | ||
//calling handleDeviceLogging audioEnabled as true videoEnabled as false | ||
assert.calledWith(handleDeviceLoggingSpy,true,false); | ||
assert.calledWith(handleDeviceLoggingSpy, true, false); | ||
}); | ||
|
||
it('addMedia() works correctly when video is disabled with no streams to publish', async () => { | ||
|
@@ -4416,12 +4431,11 @@ describe('plugin-meetings', () => { | |
assert.calledTwice(locusMediaRequestStub); | ||
}); | ||
|
||
|
||
it('addMedia() works correctly when both shareAudio and shareVideo is disabled with no streams publish', async () => { | ||
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging'); | ||
await meeting.addMedia({shareAudioEnabled: false, shareVideoEnabled: false}); | ||
//calling handleDeviceLogging with audioEnabled true and videoEnabled as true | ||
assert.calledWith(handleDeviceLoggingSpy,true,true); | ||
assert.calledWith(handleDeviceLoggingSpy, true, true); | ||
}); | ||
|
||
describe('publishStreams()/unpublishStreams() calls', () => { | ||
|
@@ -6998,7 +7012,10 @@ describe('plugin-meetings', () => { | |
assert.deepEqual(meeting.callStateForMetrics, {correlationId, sessionCorrelationId: ''}); | ||
meeting.setCorrelationId(uuid1); | ||
assert.equal(meeting.correlationId, uuid1); | ||
assert.deepEqual(meeting.callStateForMetrics, {correlationId: uuid1, sessionCorrelationId: ''}); | ||
assert.deepEqual(meeting.callStateForMetrics, { | ||
correlationId: uuid1, | ||
sessionCorrelationId: '', | ||
}); | ||
}); | ||
}); | ||
|
||
|
@@ -7670,11 +7687,11 @@ describe('plugin-meetings', () => { | |
id: 'stream', | ||
getTracks: () => [{id: 'track', addEventListener: sinon.stub()}], | ||
}; | ||
const simulateConnectionStateChange = (newState) => { | ||
const simulateConnectionStateChange = async (newState) => { | ||
meeting.mediaProperties.webrtcMediaConnection.getConnectionState = sinon | ||
.stub() | ||
.returns(newState); | ||
eventListeners[MediaConnectionEventNames.PEER_CONNECTION_STATE_CHANGED](); | ||
await eventListeners[MediaConnectionEventNames.PEER_CONNECTION_STATE_CHANGED](); | ||
}; | ||
|
||
beforeEach(() => { | ||
|
@@ -7930,7 +7947,7 @@ describe('plugin-meetings', () => { | |
meeting.reconnectionManager = new ReconnectionManager(meeting); | ||
meeting.reconnectionManager.iceReconnected = sinon.stub().returns(undefined); | ||
meeting.setNetworkStatus = sinon.stub().returns(undefined); | ||
meeting.statsAnalyzer = {startAnalyzer: sinon.stub()}; | ||
meeting.statsAnalyzer = {startAnalyzer: sinon.stub(), stopAnalyzer: sinon.stub()}; | ||
meeting.mediaProperties.webrtcMediaConnection = { | ||
// mock the on() method and store all the listeners | ||
on: sinon.stub().callsFake((event, listener) => { | ||
|
@@ -8005,10 +8022,10 @@ describe('plugin-meetings', () => { | |
}); | ||
|
||
describe('CONNECTION_STATE_CHANGED event when state = "Failed"', () => { | ||
const mockFailedEvent = () => { | ||
const mockFailedEvent = async () => { | ||
meeting.setupMediaConnectionListeners(); | ||
|
||
simulateConnectionStateChange(ConnectionState.Failed); | ||
await simulateConnectionStateChange(ConnectionState.Failed); | ||
}; | ||
|
||
const checkBehavioralMetricSent = (hasMediaConnectionConnectedAtLeastOnce = false) => { | ||
|
@@ -8038,6 +8055,22 @@ describe('plugin-meetings', () => { | |
assert.notCalled(webex.internal.newMetrics.submitClientEvent); | ||
checkBehavioralMetricSent(true); | ||
}); | ||
|
||
it('stop stats analyzer during reconnection ', async () => { | ||
meeting.hasMediaConnectionConnectedAtLeastOnce = true; | ||
meeting.statsAnalyzer.stopAnalyzer = sinon.stub().resolves(); | ||
meeting.reconnectionManager = { | ||
reconnect: sinon.stub().resolves(), | ||
resetReconnectionTimer: () => {} | ||
}; | ||
meeting.currentMediaStatus = { | ||
video: true | ||
}; | ||
|
||
await mockFailedEvent(); | ||
|
||
assert.calledOnce(meeting.statsAnalyzer.stopAnalyzer); | ||
}); | ||
}); | ||
|
||
describe('should send correct metrics for ROAP_FAILURE event', () => { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider enhancing error handling and cleanup sequence
The error handling and cleanup sequence in
cleanUpBeforeReconnection()
could be more robust: