diff --git a/openvidu-test-e2e/pom.xml b/openvidu-test-e2e/pom.xml index ab50f6a4..3b01434d 100644 --- a/openvidu-test-e2e/pom.xml +++ b/openvidu-test-e2e/pom.xml @@ -62,6 +62,18 @@ 3.141.59 + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M3 + + + + + org.junit.jupiter diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduEventManager.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduEventManager.java index 8f7f49c6..d45ff599 100644 --- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduEventManager.java +++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduEventManager.java @@ -95,12 +95,27 @@ public class OpenViduEventManager { Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) { + if (ex.getClass().getSimpleName().equals("UnhandledAlertException") + && ex.getMessage().contains("unexpected alert open")) { + stopPolling(false); + System.err.println("Alert opened. Waiting 1 second and restarting polling"); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + startPolling(); + } if (ex.getClass().getSimpleName().equals("NoSuchSessionException")) { System.err.println("Disposing driver when running 'executeScript'"); } } }; + if (this.pollingThread != null) { + this.pollingThread.interrupt(); + } + this.pollingThread = new Thread(() -> { while (!this.isInterrupted.get()) { this.getEventsFromBrowser(); @@ -116,12 +131,15 @@ public class OpenViduEventManager { this.pollingThread.start(); } - public void stopPolling() { + public void stopPolling(boolean stopThread) { this.eventCallbacks.clear(); this.eventCountdowns.clear(); this.eventNumbers.clear(); - this.isInterrupted.set(true); - this.pollingThread.interrupt(); + + if (stopThread) { + this.isInterrupted.set(true); + this.pollingThread.interrupt(); + } } public void on(String eventName, Consumer callback) { 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 49561033..2b01d7e7 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 @@ -60,6 +60,7 @@ import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; +import org.openqa.selenium.Alert; import org.openqa.selenium.By; import org.openqa.selenium.Dimension; import org.openqa.selenium.Keys; @@ -99,7 +100,7 @@ import io.openvidu.test.e2e.browser.ChromeAndroidUser; import io.openvidu.test.e2e.browser.ChromeUser; import io.openvidu.test.e2e.browser.FirefoxUser; import io.openvidu.test.e2e.browser.OperaUser; -import io.openvidu.test.e2e.utils.CommandLineExecuter; +import io.openvidu.test.e2e.utils.CommandLineExecutor; import io.openvidu.test.e2e.utils.CustomHttpClient; import io.openvidu.test.e2e.utils.MultimediaFileMetadata; import io.openvidu.test.e2e.utils.Unzipper; @@ -126,12 +127,13 @@ public class OpenViduTestAppE2eTest { BrowserUser user; Collection otherUsers = new ArrayList<>(); volatile static boolean isRecordingTest; + volatile static boolean isKurentoRestartTest; private static OpenVidu OV; @BeforeAll() static void setupAll() { - String ffmpegOutput = new CommandLineExecuter().executeCommand("which ffmpeg"); + String ffmpegOutput = new CommandLineExecutor().executeCommand("which ffmpeg"); if (ffmpegOutput == null || ffmpegOutput.isEmpty()) { log.error("ffmpeg package is not installed in the host machine"); Assert.fail(); @@ -186,7 +188,10 @@ public class OpenViduTestAppE2eTest { this.user = new ChromeAndroidUser("TestUser", 50); break; case "chromeAlternateScreenShare": - this.user = new ChromeUser("TestUser", 50, "OpenVidu TestApp"); + this.user = new ChromeUser("TestUser", 50, "OpenVidu TestApp", false); + break; + case "chromeAsRoot": + this.user = new ChromeUser("TestUser", 50, "Entire screen", true); break; default: this.user = new ChromeUser("TestUser", 50); @@ -250,6 +255,9 @@ public class OpenViduTestAppE2eTest { } isRecordingTest = false; } + if (isKurentoRestartTest) { + new CommandLineExecutor().executeCommand("sudo service kurento-media-server restart"); + } } @Test @@ -2558,6 +2566,150 @@ public class OpenViduTestAppE2eTest { + "'openviduRecordingVersion':'STR','openviduRecordingPath':'STR','openviduRecordingPublicAccess':false,'openviduRecordingNotification':'STR','openviduRecordingCustomLayout':'STR','openviduRecordingAutostopTimeout':0}"); } + @Test + @DisplayName("Kurento reconnect test") + void kurentoReconnectTest() throws Exception { + isRecordingTest = true; + isKurentoRestartTest = true; + + log.info("Kurento reconnect test"); + + List sessions = OV.getActiveSessions(); + Assert.assertEquals("Expected no active sessions but found " + sessions.size(), 0, sessions.size()); + + final CommandLineExecutor exec = new CommandLineExecutor(); + + exec.executeCommand("sudo service kurento-media-server stop"); + + OV.fetch(); + + setupBrowser("chromeAsRoot"); + + // Connect one publisher with no connection to KMS + + user.getDriver().findElement(By.id("add-user-btn")).click(); + user.getDriver().findElement(By.className("subscribe-remote-check")).click(); + user.getDriver().findElement(By.className("join-btn")).click(); + + user.getWaiter().until(ExpectedConditions.alertIsPresent()); + Alert alert = user.getDriver().switchTo().alert(); + + final String alertMessage = "Exception connecting to WebSocket server ws://localhost:8888/kurento"; + Assert.assertTrue("Alert message wrong. Expected to contain: \"" + alertMessage + "\". Actual message: \"" + + alert.getText() + "\"", alert.getText().contains(alertMessage)); + alert.accept(); + + user.getDriver().findElement(By.id("remove-user-btn")).sendKeys(Keys.ENTER); + + exec.executeCommand("sudo service kurento-media-server start"); + Thread.sleep(3000); + + // Connect one subscriber with connection to KMS -> restart KMS -> connect a + // publisher -> restart KMS -> check streamDestroyed events + + user.getDriver().findElement(By.id("add-user-btn")).click(); + user.getDriver().findElements(By.className("publish-checkbox")).forEach(el -> el.click()); + user.getDriver().findElement(By.className("join-btn")).click(); + + user.getEventManager().waitUntilEventReaches("connectionCreated", 1); + + OV.fetch(); + sessions = OV.getActiveSessions(); + Assert.assertEquals("Expected 1 active sessions but found " + sessions.size(), 1, sessions.size()); + + exec.executeCommand("sudo service kurento-media-server restart"); + Thread.sleep(3000); + + OV.fetch(); + sessions = OV.getActiveSessions(); + Assert.assertEquals("Expected 1 active sessions but found " + sessions.size(), 1, sessions.size()); + + user.getDriver().findElement(By.id("add-user-btn")).click(); + user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .join-btn")).click(); + + user.getEventManager().waitUntilEventReaches("connectionCreated", 4); + user.getEventManager().waitUntilEventReaches("accessAllowed", 1); + user.getEventManager().waitUntilEventReaches("streamCreated", 2); + user.getEventManager().waitUntilEventReaches("streamPlaying", 2); + + final int numberOfVideos = user.getDriver().findElements(By.tagName("video")).size(); + Assert.assertEquals("Expected 2 videos but found " + numberOfVideos, 2, numberOfVideos); + Assert.assertTrue("Videos were expected to have audio and video tracks", user.getEventManager() + .assertMediaTracks(user.getDriver().findElements(By.tagName("video")), true, true)); + + OV.fetch(); + Session session = OV.getActiveSessions().get(0); + Assert.assertEquals("Expected 2 active connections but found " + session.getActiveConnections(), 2, + session.getActiveConnections().size()); + int pubs = session.getActiveConnections().stream().mapToInt(con -> con.getPublishers().size()).sum(); + int subs = session.getActiveConnections().stream().mapToInt(con -> con.getSubscribers().size()).sum(); + Assert.assertEquals("Expected 1 active publisher but found " + pubs, 1, pubs); + Assert.assertEquals("Expected 1 active subscriber but found " + subs, 1, subs); + + OV.startRecording(session.getSessionId(), + new RecordingProperties.Builder().outputMode(OutputMode.INDIVIDUAL).build()); + user.getEventManager().waitUntilEventReaches("recordingStarted", 2); + + long recStartTime = System.currentTimeMillis(); + + final CountDownLatch latch = new CountDownLatch(4); + + user.getEventManager().on("recordingStopped", (event) -> { + String reason = event.get("reason").getAsString(); + Assert.assertEquals("Expected 'recordingStopped' reason 'mediaServerDisconnect'", "mediaServerDisconnect", + reason); + latch.countDown(); + }); + user.getEventManager().on("streamDestroyed", (event) -> { + String reason = event.get("reason").getAsString(); + Assert.assertEquals("Expected 'streamDestroyed' reason 'mediaServerDisconnect'", "mediaServerDisconnect", + reason); + latch.countDown(); + }); + exec.executeCommand("sudo service kurento-media-server restart"); + long recEndTime = System.currentTimeMillis(); + user.getEventManager().waitUntilEventReaches("recordingStopped", 2); + user.getEventManager().waitUntilEventReaches("streamDestroyed", 2); + if (!latch.await(5000, TimeUnit.MILLISECONDS)) { + gracefullyLeaveParticipants(2); + fail("Waiting for 2 streamDestroyed events with reason 'mediaServerDisconnect' to happen in total"); + return; + } + user.getEventManager().off("streamDestroyed"); + + session.fetch(); + Assert.assertEquals("Expected 2 active connections but found " + session.getActiveConnections(), 2, + session.getActiveConnections().size()); + pubs = session.getActiveConnections().stream().mapToInt(con -> con.getPublishers().size()).sum(); + subs = session.getActiveConnections().stream().mapToInt(con -> con.getSubscribers().size()).sum(); + Assert.assertEquals("Expected no active publishers but found " + pubs, 0, pubs); + Assert.assertEquals("Expected no active subscribers but found " + subs, 0, subs); + + Recording rec = OV.getRecording("TestSession"); + double differenceInDuration = Math.abs(rec.getDuration() - ((recEndTime - recStartTime) / 1000)); + Assert.assertTrue("Recording duration exceeds valid value. Expected no more than 0.2 seconds, got " + + differenceInDuration, differenceInDuration < 0.2); + + this.checkIndividualRecording("/opt/openvidu/recordings/TestSession/", rec, 1, "opus", "vp8", true); + + WebElement pubBtn = user.getDriver().findElements(By.cssSelector("#openvidu-instance-1 .pub-btn")).get(0); + pubBtn.click(); + user.getEventManager().waitUntilEventReaches("streamDestroyed", 3); // This is not real, only in testapp + pubBtn.click(); + user.getEventManager().waitUntilEventReaches("streamCreated", 4); + user.getEventManager().waitUntilEventReaches("streamPlaying", 4); + + session.fetch(); + Assert.assertEquals("Expected 2 active connections but found " + session.getActiveConnections(), 2, + session.getActiveConnections().size()); + pubs = session.getActiveConnections().stream().mapToInt(con -> con.getPublishers().size()).sum(); + subs = session.getActiveConnections().stream().mapToInt(con -> con.getSubscribers().size()).sum(); + Assert.assertEquals("Expected 1 active publisher but found " + pubs, 1, pubs); + Assert.assertEquals("Expected 1 active subscriber but found " + subs, 1, subs); + + gracefullyLeaveParticipants(2); + } + private void listEmptyRecordings() { // List existing recordings (empty) user.getDriver().findElement(By.id("list-recording-btn")).click(); diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/browser/BrowserUser.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/browser/BrowserUser.java index 126eac1b..dabde00a 100644 --- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/browser/BrowserUser.java +++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/browser/BrowserUser.java @@ -73,7 +73,7 @@ public class BrowserUser { } public void dispose() { - this.eventManager.stopPolling(); + this.eventManager.stopPolling(true); this.driver.quit(); } diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/browser/ChromeUser.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/browser/ChromeUser.java index 6f1641b9..97b32c39 100644 --- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/browser/ChromeUser.java +++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/browser/ChromeUser.java @@ -32,11 +32,11 @@ import org.springframework.core.io.ClassPathResource; public class ChromeUser extends BrowserUser { public ChromeUser(String userName, int timeOfWaitInSeconds) { - this(userName, timeOfWaitInSeconds, "Entire screen"); + this(userName, timeOfWaitInSeconds, "Entire screen", false); } - public ChromeUser(String userName, int timeOfWaitInSeconds, String screenToCapture) { - this(userName, timeOfWaitInSeconds, generateScreenChromeOptions(screenToCapture)); + public ChromeUser(String userName, int timeOfWaitInSeconds, String screenToCapture, boolean runningAsRoot) { + this(userName, timeOfWaitInSeconds, generateScreenChromeOptions(screenToCapture, runningAsRoot)); } public ChromeUser(String userName, int timeOfWaitInSeconds, Path fakeVideoLocation) { @@ -66,7 +66,7 @@ public class ChromeUser extends BrowserUser { this.configureDriver(); } - private static ChromeOptions generateScreenChromeOptions(String screenToCapture) { + private static ChromeOptions generateScreenChromeOptions(String screenToCapture, boolean runningAsRoot) { ChromeOptions options = new ChromeOptions(); // This flag avoids to grant the user media options.addArguments("--use-fake-ui-for-media-stream"); @@ -80,6 +80,11 @@ public class ChromeUser extends BrowserUser { } catch (IOException e) { e.printStackTrace(); } + + if (runningAsRoot) { + options.addArguments("--no-sandbox"); + } + return options; } diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/utils/CommandLineExecuter.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/utils/CommandLineExecutor.java similarity index 97% rename from openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/utils/CommandLineExecuter.java rename to openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/utils/CommandLineExecutor.java index 3aabc271..2c100997 100644 --- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/utils/CommandLineExecuter.java +++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/utils/CommandLineExecutor.java @@ -20,7 +20,7 @@ package io.openvidu.test.e2e.utils; import java.io.BufferedReader; import java.io.InputStreamReader; -public class CommandLineExecuter { +public class CommandLineExecutor { public String executeCommand(String command) { String output = ""; diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/utils/MultimediaFileMetadata.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/utils/MultimediaFileMetadata.java index b55f42ce..e95b2545 100644 --- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/utils/MultimediaFileMetadata.java +++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/utils/MultimediaFileMetadata.java @@ -28,7 +28,7 @@ public class MultimediaFileMetadata { private static final Logger log = LoggerFactory.getLogger(MultimediaFileMetadata.class); - private CommandLineExecuter executer = new CommandLineExecuter(); + private CommandLineExecutor executer = new CommandLineExecutor(); private JsonParser parser = new JsonParser(); private JsonObject json;