mirror of https://github.com/OpenVidu/openvidu.git
openvidu-test-e2e: add connectionQualityLostMeetPublisherSubscriberNotLostTest
The same check as connectionQualityLostPublisherSubscriberNotLostTest, but with the media OpenVidu Meet publishes. The Meet bundle currently deployed runs its Room with adaptiveStream and dynacast, a 720p camera and livekit-client's publish defaults (simulcast, DTX, RED, VP8). The connection-quality tests turned simulcast, dynacast and adaptiveStream off, so the camera was a single 640x480 layer. connectionQualityTest() gets an overload with a videoOptimizations flag: simulcast, dynacast and adaptiveStream enabled for both users, with a 1280x720 camera published as three VP8 simulcast layers. The existing 4-argument form passes false, so the other connection-quality tests are unchanged. The body of the previous test moves to lostPublisherDoesNotMakeSubscriberLost(videoOptimizations), which both tests call. With a simulcast camera the video track does not trigger the bug. mediasoup zeroes the score of a silent Producer with several layers within 1.5 s, and only the audio track keeps a frozen score. That also makes the publisher LOST two server quality updates earlier than with a single layer, so the subscriber's wrong LOST arrives 10 s after the publisher's. The wait after the publisher is LOST therefore goes from 10 s to 15 s; with 10 s the check ran just as that LOST arrived. Caveat: the publisher's livekit-client (>= 2.22) does its full reconnect in that same update, 10 s after its first LOST. A server with the bug therefore fails this test only while its quality update reaches the subscriber before the publisher's Leave does. It won by 10-15 ms in all 4 runs, but if it ever loses, a buggy server passes; a correct server can never fail. The single-layer test has no such race. Verified against the local Compose deployment: - mediasoup, server image built before the openvidu-pro-livekit fix: the new test failed 4 of 4 times. RegularUser went EXCELLENT -> LOST through its audio consumer, while its video consumer read producer score 0 and stayed EXCELLENT. - mediasoup with the fix, and pion: both tests pass. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>pull/911/head
parent
ae08286d60
commit
86300ec00e
|
|
@ -938,6 +938,22 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
|
|
||||||
private Pair<OpenViduTestappUser, OpenViduTestappUser> connectionQualityTest(boolean isPublisher,
|
private Pair<OpenViduTestappUser, OpenViduTestappUser> connectionQualityTest(boolean isPublisher,
|
||||||
boolean isSubscriber, Integer outboundPacketLoss, Integer inboundPacketLoss) throws Exception {
|
boolean isSubscriber, Integer outboundPacketLoss, Integer inboundPacketLoss) throws Exception {
|
||||||
|
return connectionQualityTest(isPublisher, isSubscriber, outboundPacketLoss, inboundPacketLoss, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code videoOptimizations} means simulcast, dynacast and adaptiveStream
|
||||||
|
* enabled for both users, as OpenVidu Meet has them, with a 1280x720 camera, so
|
||||||
|
* the camera is published as three VP8 simulcast layers. Without them the camera
|
||||||
|
* is a single 640x480 layer. Audio is the same in both cases (Opus with the
|
||||||
|
* livekit-client defaults, DTX and RED).
|
||||||
|
*/
|
||||||
|
private Pair<OpenViduTestappUser, OpenViduTestappUser> connectionQualityTest(boolean isPublisher,
|
||||||
|
boolean isSubscriber, Integer outboundPacketLoss, Integer inboundPacketLoss, boolean videoOptimizations)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
final Integer width = videoOptimizations ? 1280 : null;
|
||||||
|
final Integer height = videoOptimizations ? 720 : null;
|
||||||
|
|
||||||
// Connect to LiveKit server from a secure URL
|
// Connect to LiveKit server from a secure URL
|
||||||
String secureLivekitUrlFromOpenViduLocalDeployment = getLivekitWssUrlFromReadyCheckContainer();
|
String secureLivekitUrlFromOpenViduLocalDeployment = getLivekitWssUrlFromReadyCheckContainer();
|
||||||
|
|
@ -974,7 +990,8 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
throw new IllegalArgumentException("At least one of isPublisher or isSubscriber must be true");
|
throw new IllegalArgumentException("At least one of isPublisher or isSubscriber must be true");
|
||||||
}
|
}
|
||||||
if (isPublisher) {
|
if (isPublisher) {
|
||||||
this.addPublisher(punchbagUser, isSubscriber, false, false, false, true, true, null, null, null);
|
this.addPublisher(punchbagUser, isSubscriber, videoOptimizations, videoOptimizations, videoOptimizations,
|
||||||
|
true, true, width, height, null);
|
||||||
} else {
|
} else {
|
||||||
// Publish audio only (and keep subscribing) so the OTHER user subscribes to
|
// Publish audio only (and keep subscribing) so the OTHER user subscribes to
|
||||||
// PunchbagUser and therefore also receives its connectionQualityChanged events
|
// PunchbagUser and therefore also receives its connectionQualityChanged events
|
||||||
|
|
@ -982,7 +999,8 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
// subscribed to. PunchbagUser still subscribes to RegularUser's audio+video,
|
// subscribed to. PunchbagUser still subscribes to RegularUser's audio+video,
|
||||||
// which is the downlink actually impaired in this scenario; the extra audio
|
// which is the downlink actually impaired in this scenario; the extra audio
|
||||||
// uplink stays clean and does not affect its (min-based) quality.
|
// uplink stays clean and does not affect its (min-based) quality.
|
||||||
this.addPublisher(punchbagUser, true, false, false, false, true, false, null, null, null);
|
this.addPublisher(punchbagUser, true, videoOptimizations, videoOptimizations, videoOptimizations, true,
|
||||||
|
false, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebElement participantNameInput = punchbagUser.getDriver().findElement(By.id("participant-name-input-0"));
|
WebElement participantNameInput = punchbagUser.getDriver().findElement(By.id("participant-name-input-0"));
|
||||||
|
|
@ -1022,9 +1040,10 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
regularUser.getEventManager().startPolling();
|
regularUser.getEventManager().startPolling();
|
||||||
|
|
||||||
if (isSubscriber) {
|
if (isSubscriber) {
|
||||||
this.addPublisher(regularUser, true, false, false, false, true, true, null, null, null);
|
this.addPublisher(regularUser, true, videoOptimizations, videoOptimizations, videoOptimizations, true, true,
|
||||||
|
width, height, null);
|
||||||
} else {
|
} else {
|
||||||
this.addSubscriber(regularUser, false);
|
this.addSubscriber(regularUser, videoOptimizations);
|
||||||
}
|
}
|
||||||
participantNameInput = regularUser.getDriver().findElement(By.id("participant-name-input-0"));
|
participantNameInput = regularUser.getDriver().findElement(By.id("participant-name-input-0"));
|
||||||
participantNameInput.clear();
|
participantNameInput.clear();
|
||||||
|
|
@ -1193,8 +1212,24 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
|
|
||||||
log.info("ConnectionQuality LOST publisher does not make its subscriber LOST");
|
log.info("ConnectionQuality LOST publisher does not make its subscriber LOST");
|
||||||
|
|
||||||
|
// Single-layer camera: both published tracks keep a frozen score in mediasoup
|
||||||
|
lostPublisherDoesNotMakeSubscriberLost(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("ConnectionQuality LOST publisher with OpenVidu Meet media does not make its subscriber LOST")
|
||||||
|
void connectionQualityLostMeetPublisherSubscriberNotLostTest() throws Exception {
|
||||||
|
|
||||||
|
log.info("ConnectionQuality LOST publisher with OpenVidu Meet media does not make its subscriber LOST");
|
||||||
|
|
||||||
|
lostPublisherDoesNotMakeSubscriberLost(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void lostPublisherDoesNotMakeSubscriberLost(boolean videoOptimizations) throws Exception {
|
||||||
|
|
||||||
// PunchbagUser publishes audio and video, RegularUser only subscribes to them
|
// PunchbagUser publishes audio and video, RegularUser only subscribes to them
|
||||||
Pair<OpenViduTestappUser, OpenViduTestappUser> users = connectionQualityTest(true, false, null, null);
|
Pair<OpenViduTestappUser, OpenViduTestappUser> users = connectionQualityTest(true, false, null, null,
|
||||||
|
videoOptimizations);
|
||||||
OpenViduTestappUser punchbagUser = users.getLeft();
|
OpenViduTestappUser punchbagUser = users.getLeft();
|
||||||
OpenViduTestappUser regularUser = users.getRight();
|
OpenViduTestappUser regularUser = users.getRight();
|
||||||
|
|
||||||
|
|
@ -1204,8 +1239,11 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
try {
|
try {
|
||||||
waitUntilConnectionQuality(regularUser, 0, "PunchbagUser", q -> q.contains("lost"), 45,
|
waitUntilConnectionQuality(regularUser, 0, "PunchbagUser", q -> q.contains("lost"), 45,
|
||||||
"Expected the publisher's connection quality to reach LOST");
|
"Expected the publisher's connection quality to reach LOST");
|
||||||
// Leave a couple more server quality ticks (every 5 s) to be reported
|
// Leave three more server quality ticks (every 5 s) to be reported. With a
|
||||||
Thread.sleep(10000);
|
// single-layer camera the publisher goes LOST when its media is found stale,
|
||||||
|
// in the same tick as a subscriber wrongly would. A silent simulcast camera is
|
||||||
|
// LOST two ticks before that, when mediasoup zeroes its score
|
||||||
|
Thread.sleep(15000);
|
||||||
|
|
||||||
// RegularUser's own network is not impaired, so the publisher's outage must
|
// RegularUser's own network is not impaired, so the publisher's outage must
|
||||||
// not be reported as LOST for RegularUser itself. Its instance lists its own
|
// not be reported as LOST for RegularUser itself. Its instance lists its own
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue