From 73c3d581bd14bc0f062aae011c425c1aea1de2e2 Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Wed, 2 Sep 2026 19:02:57 +0200 Subject: [PATCH] openvidu-test-e2e: improve test execution time --- .../test/e2e/OpenViduTestAppE2eTest.java | 170 +++++++++++++----- 1 file changed, 124 insertions(+), 46 deletions(-) diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java index 0f90d9dc1..879161ed9 100644 --- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java +++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java @@ -93,6 +93,8 @@ import static org.openqa.selenium.OutputType.BASE64; @ExtendWith(SpringExtension.class) public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { + private static final long WAIT_UNTIL_MAX_MILLIS = 20000; + @BeforeAll() protected static void setupAll() throws Exception { checkFfmpegInstallation(); @@ -3379,33 +3381,42 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { @Test @DisplayName("SVC VP9 (L3T3_KEY)") void svcVP9L3T3_KEYTest() throws Exception { - svcTest("VP9", "L3T3_KEY"); + svcTest("VP9", "L3T3_KEY", false); } @Test @DisplayName("SVC AV1 (L3T3_KEY)") void svcAV1L3T3_KEYTest() throws Exception { - svcTest("AV1", "L3T3_KEY"); + svcTest("AV1", "L3T3_KEY", false); } @Test @DisplayName("SVC VP9 (L2T2)") void svcVP9L2T2Test() throws Exception { - svcTest("VP9", "L2T2"); + svcTest("VP9", "L2T2", false); } @Test @DisplayName("SVC AV1 (L2T2)") void svcAV1L2T2Test() throws Exception { - svcTest("AV1", "L2T2"); + svcTest("AV1", "L2T2", false); } - private void svcTest(String codec, String scalabilityMode) throws Exception { + @Test + @DisplayName("SVC AV1 (L2T2) with adaptiveStream") + void svcAV1L2T2WithAdaptiveStreamTest() throws Exception { + svcTest("AV1", "L2T2", true); + } + + private void svcTest(String codec, String scalabilityMode, boolean adaptiveStream) throws Exception { final String codecUpperCase = codec.toUpperCase(); + final long testStart = System.currentTimeMillis(); + log.info("[SVC] START test: codec={}, scalabilityMode={}, adaptiveStream={}", codec, scalabilityMode, + adaptiveStream); OpenViduTestappUser user = setupBrowserAndConnectToOpenViduTestapp("chrome"); this.addOnlyPublisherVideo(user, false, false, true, scalabilityMode); this.forceCodec(user, 0, codec); - this.addSubscriber(user, false); + this.addSubscriber(user, adaptiveStream); user.getDriver().findElements(By.className("connect-btn")).forEach(el -> el.sendKeys(Keys.ENTER)); user.getEventManager().waitUntilEventReaches("localTrackSubscribed", "ParticipantEvent", 1); user.getEventManager().waitUntilEventReaches("trackSubscribed", "ParticipantEvent", 1); @@ -3428,24 +3439,54 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { // Validate SVC by dynamically switching subscriber quality and checking // subscriber frameWidth transitions. + // With adaptiveStream disabled, quality is switched manually through the + // "max-video-quality" selector, which lets HIGH/MEDIUM/LOW be requested + // explicitly. With adaptiveStream enabled, that manual selector has no + // effect: the spatial layer is instead derived from the rendered size of + // the subscriber's video element int spatialLayers = Integer.parseInt(scalabilityMode.substring(1, 2)); int highWidth = this.getSubscriberVideoFrameWidth(user, subscriberVideo); user.getDriver().findElement(By.cssSelector("#close-dialog-btn")).click(); Thread.sleep(300); - if (spatialLayers >= 3) { + if (adaptiveStream) { + this.switchSubscriberSpatialLayer(user, subscriberVideo, adaptiveStream, "HIGH"); + this.waitUntilSubscriberFrameWidthChanges(user, subscriberVideo, highWidth, true); + this.waitUntilSubscriberFramesDecodedIncrease(user, subscriberVideo); + int bigWidth = this.getSubscriberVideoFrameWidth(user, subscriberVideo); + user.getDriver().findElement(By.cssSelector("#close-dialog-btn")).click(); + Thread.sleep(300); + + this.switchSubscriberSpatialLayer(user, subscriberVideo, adaptiveStream, "LOW"); + this.waitUntilSubscriberFrameWidthChanges(user, subscriberVideo, bigWidth, false); + this.waitUntilSubscriberFramesDecodedIncrease(user, subscriberVideo); + int smallWidth = this.getSubscriberVideoFrameWidth(user, subscriberVideo); + Assertions.assertTrue(bigWidth > smallWidth, + "Expected the zoomed-in width to exceed the zoomed-out width, but got big=" + bigWidth + + ", small=" + smallWidth); + user.getDriver().findElement(By.cssSelector("#close-dialog-btn")).click(); + Thread.sleep(300); + + this.switchSubscriberSpatialLayer(user, subscriberVideo, adaptiveStream, "HIGH"); + this.waitUntilSubscriberFrameWidthChanges(user, subscriberVideo, smallWidth, true); + this.waitUntilSubscriberFramesDecodedIncrease(user, subscriberVideo); + int finalBigWidth = this.getSubscriberVideoFrameWidth(user, subscriberVideo); + Assertions.assertEquals(bigWidth, finalBigWidth, + "Expected the zoomed-in width to be restored, but got first=" + bigWidth + ", second=" + + finalBigWidth); + } else if (spatialLayers >= 3) { // 3 spatial layers (e.g. L3T3_KEY): layers are LOW, MEDIUM, HIGH. // HIGH → MEDIUM → LOW, each a distinct lower resolution. - user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 #max-video-quality")).click(); - this.waitForBackdropAndClick(user, "mat-option.mode-MEDIUM"); + this.switchSubscriberSpatialLayer(user, subscriberVideo, adaptiveStream, "MEDIUM"); this.waitUntilSubscriberFrameWidthChanges(user, subscriberVideo, highWidth, false); + this.waitUntilSubscriberFramesDecodedIncrease(user, subscriberVideo); int mediumWidth = this.getSubscriberVideoFrameWidth(user, subscriberVideo); user.getDriver().findElement(By.cssSelector("#close-dialog-btn")).click(); Thread.sleep(300); - user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 #max-video-quality")).click(); - this.waitForBackdropAndClick(user, "mat-option.mode-LOW"); + this.switchSubscriberSpatialLayer(user, subscriberVideo, adaptiveStream, "LOW"); this.waitUntilSubscriberFrameWidthChanges(user, subscriberVideo, mediumWidth, false); + this.waitUntilSubscriberFramesDecodedIncrease(user, subscriberVideo); int lowWidth = this.getSubscriberVideoFrameWidth(user, subscriberVideo); Assertions.assertTrue(highWidth > mediumWidth && mediumWidth > lowWidth, "Expected HIGH > MEDIUM > LOW frame widths, but got HIGH=" + highWidth @@ -3453,17 +3494,16 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { user.getDriver().findElement(By.cssSelector("#close-dialog-btn")).click(); Thread.sleep(300); - user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 #max-video-quality")).click(); - this.waitForBackdropAndClick(user, "mat-option.mode-HIGH"); + this.switchSubscriberSpatialLayer(user, subscriberVideo, adaptiveStream, "HIGH"); this.waitUntilSubscriberFrameWidthChanges(user, subscriberVideo, lowWidth, true); + this.waitUntilSubscriberFramesDecodedIncrease(user, subscriberVideo); } else { // 2 spatial layers (e.g. L2T2): layers are LOW and MEDIUM only. // HIGH and MEDIUM both map to the highest spatial layer (same width). // Only LOW gives a distinct lower resolution. // 1. Switch to MEDIUM: should stay at the same width as HIGH - user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 #max-video-quality")).click(); - this.waitForBackdropAndClick(user, "mat-option.mode-MEDIUM"); + this.switchSubscriberSpatialLayer(user, subscriberVideo, adaptiveStream, "MEDIUM"); Thread.sleep(4000); int mediumWidth = this.getSubscriberVideoFrameWidth(user, subscriberVideo); Assertions.assertEquals(highWidth, mediumWidth, @@ -3471,11 +3511,12 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { + highWidth + ", MEDIUM=" + mediumWidth); user.getDriver().findElement(By.cssSelector("#close-dialog-btn")).click(); Thread.sleep(300); + this.waitUntilSubscriberFramesDecodedIncrease(user, subscriberVideo); // 2. Switch to LOW: should decrease - user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 #max-video-quality")).click(); - this.waitForBackdropAndClick(user, "mat-option.mode-LOW"); + this.switchSubscriberSpatialLayer(user, subscriberVideo, adaptiveStream, "LOW"); this.waitUntilSubscriberFrameWidthChanges(user, subscriberVideo, mediumWidth, false); + this.waitUntilSubscriberFramesDecodedIncrease(user, subscriberVideo); int lowWidth = this.getSubscriberVideoFrameWidth(user, subscriberVideo); Assertions.assertTrue(highWidth > lowWidth, "Expected HIGH > LOW frame widths, but got HIGH=" + highWidth + ", LOW=" + lowWidth); @@ -3483,23 +3524,48 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { Thread.sleep(300); // 3. Switch to MEDIUM: should increase back to highest spatial layer - user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 #max-video-quality")).click(); - this.waitForBackdropAndClick(user, "mat-option.mode-MEDIUM"); + this.switchSubscriberSpatialLayer(user, subscriberVideo, adaptiveStream, "MEDIUM"); this.waitUntilSubscriberFrameWidthChanges(user, subscriberVideo, lowWidth, true); + this.waitUntilSubscriberFramesDecodedIncrease(user, subscriberVideo); int mediumWidth2 = this.getSubscriberVideoFrameWidth(user, subscriberVideo); user.getDriver().findElement(By.cssSelector("#close-dialog-btn")).click(); Thread.sleep(300); // 4. Switch to HIGH: should stay at the same width as MEDIUM - user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 #max-video-quality")).click(); - this.waitForBackdropAndClick(user, "mat-option.mode-HIGH"); + this.switchSubscriberSpatialLayer(user, subscriberVideo, adaptiveStream, "HIGH"); Thread.sleep(4000); int finalHighWidth = this.getSubscriberVideoFrameWidth(user, subscriberVideo); Assertions.assertEquals(mediumWidth2, finalHighWidth, "With " + spatialLayers + " spatial layers, HIGH should equal MEDIUM width, but got MEDIUM=" + mediumWidth2 + ", HIGH=" + finalHighWidth); + this.waitUntilSubscriberFramesDecodedIncrease(user, subscriberVideo); } + gracefullyLeaveParticipants(user, 2); + + log.info("[SVC] END test: codec={}, scalabilityMode={}, adaptiveStream={}, totalDuration={} ms", codec, + scalabilityMode, adaptiveStream, System.currentTimeMillis() - testStart); + } + + // With adaptiveStream disabled, the subscriber's preferred quality is + // switched manually through the "max-video-quality" selector. With + // adaptiveStream enabled, that manual selector is ignored by the client SDK, + // which instead derives the desired spatial layer from the rendered size of + // the subscriber's video element, always picking the smallest layer that is + // sufficient for that size. Only "LOW" (small element) and "HIGH" (large + // element) are meaningful in that case. + private void switchSubscriberSpatialLayer(OpenViduTestappUser user, WebElement subscriberVideo, + boolean adaptiveStream, String quality) { + if (adaptiveStream) { + if ("LOW".equals(quality)) { + changeElementSize(user, subscriberVideo, 80, 100); + } else { + changeElementSize(user, subscriberVideo, 1000, 700); + } + } else { + user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 #max-video-quality")).click(); + this.waitForBackdropAndClick(user, "mat-option.mode-" + quality); + } } @Test @@ -4364,18 +4430,26 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { private T getSubscriberVideoLayerStat(OpenViduTestappUser user, WebElement subscriberVideo, String field, java.util.function.Function extractor) { - waitUntilVideoLayersNotEmpty(user, subscriberVideo); - final java.util.concurrent.atomic.AtomicReference value = new java.util.concurrent.atomic.AtomicReference<>(); - this.waitUntilAux(user, subscriberVideo, () -> { - JsonElement element = getLayersAsJsonArray(user, subscriberVideo).get(0).getAsJsonObject().get(field); - if (element != null && !element.isJsonNull()) { - value.set(extractor.apply(element)); - return true; + final long deadline = System.currentTimeMillis() + WAIT_UNTIL_MAX_MILLIS; + JsonElement element = null; + do { + try { + element = getLayersAsJsonArray(user, subscriberVideo).get(0).getAsJsonObject().get(field); + } catch (Exception e) { + element = null; } - return false; - }, "Timeout waiting for " + field + " to exist"); - openInfoDialog(user, subscriberVideo); - return value.get(); + if (element == null || element.isJsonNull()) { + try { + Thread.sleep(250); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } while ((element == null || element.isJsonNull()) && System.currentTimeMillis() < deadline); + if (element == null || element.isJsonNull()) { + Assertions.fail("Timeout waiting for " + field + " to exist"); + } + return extractor.apply(element); } // If rid is null, retrieve the first layer @@ -4406,10 +4480,10 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { } private void waitUntilVideoLayersNotEmpty(OpenViduTestappUser user, WebElement videoElement) { - this.waitUntilAux(user, videoElement, - () -> !getLayersAsString(user, videoElement).isBlank() - && !getLayersAsJsonArray(user, videoElement).isEmpty(), - "Timeout waiting video layers to not be empty"); + this.waitUntilAux(user, videoElement, () -> { + String value = getLayersAsString(user, videoElement); + return !value.isBlank() && !JsonParser.parseString(value).getAsJsonArray().isEmpty(); + }, "Timeout waiting video layers to not be empty"); } private void waitUntilSubscriberFramesPerSecondNotZero(OpenViduTestappUser user, WebElement videoElement) { @@ -4502,13 +4576,10 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { private void waitUntilAux(OpenViduTestappUser user, WebElement videoElement, Callable breakFromLoopFunction, String errMsg) { try { - final int maxWaitMillis = 6000; - final int intervalWait = 250; - final int MAX_ITERATIONS = maxWaitMillis / intervalWait; - int iteration = 0; + final long intervalWait = 250; + final long deadline = System.currentTimeMillis() + WAIT_UNTIL_MAX_MILLIS; boolean breakFromLoop = false; - while (!breakFromLoop && iteration < MAX_ITERATIONS) { - iteration++; + while (!breakFromLoop && System.currentTimeMillis() < deadline) { try { breakFromLoop = breakFromLoopFunction.call(); } catch (Exception e1) { @@ -4543,21 +4614,28 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { private void openInfoDialog(OpenViduTestappUser user, WebElement video) { String videoId = video.getDomProperty("id"); // Open the track info dialog if required + boolean dialogWasOpened; if (!user.getDriver().findElements(By.cssSelector("app-info-dialog")).isEmpty()) { // Dialog already opened if (!user.getDriver().findElement(By.cssSelector("#subtitle")).getText().equals(videoId)) { // Wrong dialog this.waitForBackdropAndClick(user, "#close-dialog-btn"); this.waitForBackdropAndClick(user, "#" + videoId + " ~ .bottom-div .video-track-info"); + dialogWasOpened = true; + } else { + dialogWasOpened = false; } } else { // Dialog is not opened this.waitForBackdropAndClick(user, "#" + videoId + " ~ .bottom-div .video-track-info"); + dialogWasOpened = true; } - try { - Thread.sleep(300); - } catch (InterruptedException e) { - e.printStackTrace(); + if (dialogWasOpened) { + try { + Thread.sleep(300); + } catch (InterruptedException e) { + e.printStackTrace(); + } } }