mirror of https://github.com/OpenVidu/openvidu.git
openvidu-test-e2e: add e2e test for individual recordings abruptly stopped
parent
46050c40b4
commit
216d0e399f
|
|
@ -17,22 +17,41 @@
|
|||
|
||||
package io.openvidu.test.browsers;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.openqa.selenium.Capabilities;
|
||||
import org.openqa.selenium.NoSuchSessionException;
|
||||
import org.openqa.selenium.UnexpectedAlertBehaviour;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebDriverException;
|
||||
import org.openqa.selenium.chrome.ChromeDriver;
|
||||
import org.openqa.selenium.chrome.ChromeOptions;
|
||||
import org.openqa.selenium.chromium.HasCdp;
|
||||
import org.openqa.selenium.remote.Augmenter;
|
||||
import org.openqa.selenium.remote.RemoteWebDriver;
|
||||
|
||||
public class ChromeUser extends BrowserUser {
|
||||
|
||||
private Long chromeDriverPid;
|
||||
|
||||
public ChromeUser(String userName, int timeOfWaitInSeconds, boolean headless) {
|
||||
this(userName, timeOfWaitInSeconds, generateDefaultScreenChromeOptions(), headless);
|
||||
}
|
||||
|
|
@ -92,7 +111,9 @@ public class ChromeUser extends BrowserUser {
|
|||
}
|
||||
} else {
|
||||
log.info("Using local web driver");
|
||||
this.driver = new ChromeDriver(options);
|
||||
ChromeDriver chromeDriver = new ChromeDriver(options);
|
||||
this.driver = chromeDriver;
|
||||
this.chromeDriverPid = getChromeDriverPid(chromeDriver);
|
||||
}
|
||||
|
||||
this.driver.manage().timeouts().scriptTimeout(Duration.ofSeconds(timeOfWaitInSeconds));
|
||||
|
|
@ -135,4 +156,184 @@ public class ChromeUser extends BrowserUser {
|
|||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates an abrupt Chrome crash by forcefully killing the browser process.
|
||||
* Works for both local and remote WebDriver instances.
|
||||
*
|
||||
* @throws IOException if process killing fails
|
||||
*/
|
||||
public void simulateCrash() throws IOException {
|
||||
String REMOTE_URL = System.getProperty("REMOTE_URL_CHROME");
|
||||
if (REMOTE_URL != null) {
|
||||
// Remote WebDriver: Use Chrome DevTools Protocol to crash the browser
|
||||
simulateRemoteCrash();
|
||||
} else {
|
||||
// Local WebDriver: Kill the Chrome process directly
|
||||
simulateLocalCrash();
|
||||
}
|
||||
log.info("Simulated Chrome crash for user {}", this.clientData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates crash for local Chrome instance by killing the process
|
||||
*/
|
||||
private void simulateLocalCrash() throws IOException {
|
||||
ProcessHandle.of(chromeDriverPid).ifPresent(ph -> {
|
||||
// Kill all descendant processes (the actual Chrome browser processes)
|
||||
ph.descendants().forEach(child -> child.destroyForcibly());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates crash for remote Chrome instance using CDP
|
||||
*/
|
||||
private void simulateRemoteCrash() {
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
try {
|
||||
CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
Map<String, Object> params = Collections.emptyMap();
|
||||
HasCdp cdp = (driver instanceof HasCdp)
|
||||
? (HasCdp) driver
|
||||
: (HasCdp) new Augmenter().augment(driver);
|
||||
cdp.executeCdpCommand("Browser.crash", params);
|
||||
} catch (Exception ignored) {
|
||||
// Expected if browser crashes while executing the command
|
||||
}
|
||||
}, executor);
|
||||
|
||||
try {
|
||||
cf.get(2, TimeUnit.SECONDS);
|
||||
log.info("Browser crash command executed (response received)");
|
||||
} catch (TimeoutException te) {
|
||||
log.info("Browser crash command sent (no response as expected)");
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("Interrupted while sending crash command", ie);
|
||||
} catch (ExecutionException ee) {
|
||||
log.info("Browser crashed (execution failure)", ee.getCause());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn(
|
||||
"CDP crash command failed, attempting alternative method by directly killing \"selenium/standalone-chrome\" docker container",
|
||||
e);
|
||||
try {
|
||||
Runtime.getRuntime().exec(new String[] { "sh", "-c",
|
||||
"docker ps | grep selenium/standalone-chrome | awk '{print $1}' | xargs -I {} docker kill {}" });
|
||||
} catch (IOException ex) {
|
||||
log.error("Failed to kill remote Chrome process", ex);
|
||||
}
|
||||
} finally {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts Chrome driver process PID from ChromeDriver
|
||||
*/
|
||||
private Long getChromeDriverPid(ChromeDriver driver) {
|
||||
try {
|
||||
Capabilities caps = driver.getCapabilities();
|
||||
Object debuggerAddress = caps.getCapability("goog:chromeOptions");
|
||||
|
||||
if (debuggerAddress instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> options = (Map<String, Object>) debuggerAddress;
|
||||
Object addr = options.get("debuggerAddress");
|
||||
if (addr != null) {
|
||||
// Parse port from debugger address (e.g., "localhost:12345")
|
||||
String[] parts = addr.toString().split(":");
|
||||
if (parts.length == 2) {
|
||||
int port = Integer.parseInt(parts[1]);
|
||||
return findPidByPort(port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Find Chrome process by command line
|
||||
return findChromePidByCommandLine();
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to get Chrome PID from driver", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds Chrome PID by the port it's listening on
|
||||
*/
|
||||
private Long findPidByPort(int port) {
|
||||
try {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
Process process;
|
||||
|
||||
if (os.contains("win")) {
|
||||
process = Runtime.getRuntime().exec(new String[] { "netstat", "-ano" });
|
||||
} else {
|
||||
process = Runtime.getRuntime().exec(new String[] { "sh", "-c", "lsof -ti:" + port + " | head -n 1" });
|
||||
}
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String line;
|
||||
|
||||
if (os.contains("win")) {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.contains(":" + port + " ")) {
|
||||
String[] parts = line.trim().split("\\s+");
|
||||
return Long.parseLong(parts[parts.length - 1]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
line = reader.readLine();
|
||||
if (line != null && !line.isEmpty()) {
|
||||
return Long.parseLong(line.trim());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to find PID by port", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds Chrome PID by searching for chrome process
|
||||
*/
|
||||
private Long findChromePidByCommandLine() {
|
||||
try {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
Process process;
|
||||
|
||||
if (os.contains("win")) {
|
||||
process = Runtime.getRuntime()
|
||||
.exec(new String[] { "sh", "-c",
|
||||
"wmic process where \"name='chrome.exe'\" get ProcessId,CommandLine" });
|
||||
} else if (os.contains("mac")) {
|
||||
process = Runtime.getRuntime().exec(new String[] { "sh", "-c",
|
||||
"ps aux | grep -i chrome | grep -v grep | awk '{print $2}' | head -n 1" });
|
||||
} else {
|
||||
process = Runtime.getRuntime().exec(new String[] { "sh", "-c",
|
||||
"pgrep -f chrome | head -n 1" });
|
||||
}
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String line;
|
||||
|
||||
if (os.contains("win")) {
|
||||
reader.readLine(); // Skip header
|
||||
line = reader.readLine();
|
||||
if (line != null) {
|
||||
String[] parts = line.trim().split("\\s+");
|
||||
return Long.parseLong(parts[parts.length - 1]);
|
||||
}
|
||||
} else {
|
||||
line = reader.readLine();
|
||||
if (line != null && !line.isEmpty()) {
|
||||
return Long.parseLong(line.trim());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to find Chrome PID by command line", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ public class RecordingUtils {
|
|||
private boolean recordedFileFine(File file, Recording recording,
|
||||
Function<Map<String, Long>, Boolean> colorCheckFunction) throws IOException {
|
||||
this.checkMultimediaFile(file, recording.hasAudio(), recording.hasVideo(), recording.getDuration(),
|
||||
recording.getResolution(), recording.getFrameRate(), "aac", "h264", true);
|
||||
recording.getResolution(), recording.getFrameRate(), "aac", "h264", true, 2);
|
||||
|
||||
boolean isFine = false;
|
||||
Picture frame;
|
||||
|
|
@ -115,7 +115,8 @@ public class RecordingUtils {
|
|||
}
|
||||
|
||||
public void checkIndividualRecording(String recPath, Recording recording, int numberOfVideoFiles,
|
||||
String audioDecoder, String videoDecoder, boolean checkAudio) throws IOException {
|
||||
String audioDecoder, String videoDecoder, boolean checkAudio, long durationToleranceInSeconds)
|
||||
throws IOException {
|
||||
|
||||
// Should be only 2 files: zip and metadata
|
||||
File folder = new File(recPath);
|
||||
|
|
@ -179,7 +180,8 @@ public class RecordingUtils {
|
|||
log.info("Duration of {} according to sync metadata json file: {} s", webmFile.getName(),
|
||||
durationInSeconds);
|
||||
this.checkMultimediaFile(webmFile, recording.hasAudio(), recording.hasVideo(), durationInSeconds,
|
||||
recording.getResolution(), recording.getFrameRate(), audioDecoder, videoDecoder, checkAudio);
|
||||
recording.getResolution(), recording.getFrameRate(), audioDecoder, videoDecoder, checkAudio,
|
||||
durationToleranceInSeconds);
|
||||
webmFile.delete();
|
||||
}
|
||||
|
||||
|
|
@ -190,7 +192,9 @@ public class RecordingUtils {
|
|||
}
|
||||
|
||||
public void checkMultimediaFile(File file, boolean hasAudio, boolean hasVideo, double duration, String resolution,
|
||||
Integer frameRate, String audioDecoder, String videoDecoder, boolean checkAudio) throws IOException {
|
||||
Integer frameRate, String audioDecoder, String videoDecoder, boolean checkAudio,
|
||||
long durationToleranceInSeconds)
|
||||
throws IOException {
|
||||
// Check tracks, duration, resolution, framerate and decoders
|
||||
MultimediaFileMetadata metadata = new MultimediaFileMetadata(file.getAbsolutePath());
|
||||
|
||||
|
|
@ -225,11 +229,11 @@ public class RecordingUtils {
|
|||
df.setRoundingMode(RoundingMode.UP);
|
||||
log.info("Duration of {} according to ffmpeg: {} s", file.getName(), metadata.getDuration());
|
||||
log.info("Duration of {} according to 'duration' property: {} s", file.getName(), duration);
|
||||
log.info("Difference in s duration: {}", Math.abs(metadata.getDuration() - duration));
|
||||
final double difference = 10;
|
||||
Assertions.assertTrue(Math.abs((metadata.getDuration() - duration)) < difference,
|
||||
log.info("Difference in s duration: {}", Math.abs(duration - metadata.getDuration()));
|
||||
Assertions.assertTrue(Math.abs((duration - metadata.getDuration())) < durationToleranceInSeconds,
|
||||
"Difference between recording entity duration (" + duration + ") and real video duration ("
|
||||
+ metadata.getDuration() + ") is greater than " + difference + " in file " + file.getName());
|
||||
+ metadata.getDuration() + ") is greater than " + durationToleranceInSeconds + " in file "
|
||||
+ file.getName());
|
||||
}
|
||||
|
||||
public boolean thumbnailIsFine(File file, Function<Map<String, Long>, Boolean> colorCheckFunction) {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public class MediaNodeDockerUtils {
|
|||
DockerClient dockerClient = getDockerClient();
|
||||
ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId).withAttachStdout(true)
|
||||
.withAttachStderr(true)
|
||||
.withCmd("bash", "-c", "docker stop kms && sleep " + (millisStop / 1000) + " && docker start kms")
|
||||
.withCmd("bash", "-c", "docker kill kms && sleep " + (millisStop / 1000) + " && docker start kms")
|
||||
.exec();
|
||||
dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(new ResultCallback.Adapter<>() {
|
||||
});
|
||||
|
|
|
|||
|
|
@ -58,12 +58,14 @@ import io.openvidu.java.client.OpenViduRole;
|
|||
import io.openvidu.java.client.Recording;
|
||||
import io.openvidu.java.client.RecordingProperties;
|
||||
import io.openvidu.java.client.Session;
|
||||
import io.openvidu.test.browsers.ChromeUser;
|
||||
import io.openvidu.test.browsers.utils.CustomHttpClient;
|
||||
import io.openvidu.test.browsers.utils.RecordingUtils;
|
||||
import io.openvidu.test.browsers.utils.Unzipper;
|
||||
import io.openvidu.test.browsers.utils.layout.CustomLayoutHandler;
|
||||
import io.openvidu.test.browsers.utils.webhook.CustomWebhook;
|
||||
import io.openvidu.test.e2e.utils.TestUtils;
|
||||
import io.openvidu.test.e2e.annotations.OnlyKurento;
|
||||
|
||||
public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||
|
||||
|
|
@ -649,10 +651,13 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
}
|
||||
}
|
||||
|
||||
long accumulatedTimesWhenSartingRecordings = 0;
|
||||
|
||||
// Start the recording of the sessions
|
||||
restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start",
|
||||
"{'session':'" + sessionName + "','outputMode':'INDIVIDUAL'}", HttpURLConnection.HTTP_OK);
|
||||
user.getEventManager().waitUntilEventReaches("recordingStarted", 3);
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
// Get connectionId and streamId for one of the users configured to NOT be
|
||||
|
|
@ -671,20 +676,39 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
}
|
||||
}
|
||||
|
||||
long timeBeforeOp = System.currentTimeMillis();
|
||||
|
||||
// Generate 3 total recordings of 1 second length for the stream of the user
|
||||
// configured to NOT be recorded
|
||||
restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/" + sessionName + "/connection/" + connectionId2,
|
||||
"{'record':true}", HttpURLConnection.HTTP_OK);
|
||||
user.getEventManager().waitUntilEventReaches("connectionPropertyChanged", 1);
|
||||
|
||||
accumulatedTimesWhenSartingRecordings += System.currentTimeMillis() - timeBeforeOp;
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
timeBeforeOp = System.currentTimeMillis();
|
||||
|
||||
restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/" + sessionName + "/connection/" + connectionId2,
|
||||
"{'record':false}", HttpURLConnection.HTTP_OK);
|
||||
restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/" + sessionName + "/connection/" + connectionId2,
|
||||
"{'record':true}", HttpURLConnection.HTTP_OK);
|
||||
user.getEventManager().waitUntilEventReaches("connectionPropertyChanged", 3);
|
||||
|
||||
accumulatedTimesWhenSartingRecordings += System.currentTimeMillis() - timeBeforeOp;
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
timeBeforeOp = System.currentTimeMillis();
|
||||
restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/" + sessionName + "/connection/" + connectionId2,
|
||||
"{'record':false}", HttpURLConnection.HTTP_OK);
|
||||
restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/" + sessionName + "/connection/" + connectionId2,
|
||||
"{'record':true}", HttpURLConnection.HTTP_OK);
|
||||
user.getEventManager().waitUntilEventReaches("connectionPropertyChanged", 5);
|
||||
|
||||
accumulatedTimesWhenSartingRecordings += System.currentTimeMillis() - timeBeforeOp;
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/stop/" + sessionName, HttpURLConnection.HTTP_OK);
|
||||
|
|
@ -695,7 +719,7 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
String recPath = "/opt/openvidu/recordings/" + sessionName + "/";
|
||||
Recording recording = new OpenVidu(OpenViduTestAppE2eTest.OPENVIDU_URL, OpenViduTestAppE2eTest.OPENVIDU_SECRET)
|
||||
.getRecording(sessionName);
|
||||
this.recordingUtils.checkIndividualRecording(recPath, recording, 4, "opus", "vp8", true);
|
||||
this.recordingUtils.checkIndividualRecording(recPath, recording, 4, "opus", "vp8", true, 5);
|
||||
|
||||
// Analyze INDIVIDUAL recording metadata
|
||||
new Unzipper().unzipFile(recPath, recording.getName() + ".zip");
|
||||
|
|
@ -718,8 +742,9 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
Assertions.assertEquals(connectionId1, file.get("connectionId").getAsString(),
|
||||
"Wrong connectionId file metadata property");
|
||||
long msDuration = file.get("endTimeOffset").getAsLong() - file.get("startTimeOffset").getAsLong();
|
||||
Assertions.assertTrue((msDuration - 4000) < 1000,
|
||||
"Wrong recording duration of individual file. Difference: " + (msDuration - 4000));
|
||||
long differenceInDuration = msDuration - 4000 - accumulatedTimesWhenSartingRecordings;
|
||||
Assertions.assertTrue(differenceInDuration < 750,
|
||||
"Wrong recording duration of individual file. Difference: " + differenceInDuration);
|
||||
count1++;
|
||||
} else if (fileStreamId.equals(streamId2)) {
|
||||
// Dynamically recorded user
|
||||
|
|
@ -753,6 +778,137 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
Assertions.assertTrue(regexNames.isEmpty(), "Some expected file name didn't existed: " + regexNames.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Individual record with abrupt user disconnection")
|
||||
void individualRecordWithAbruptUserDisconnectionTest() throws Exception {
|
||||
isRecordingTest = true;
|
||||
|
||||
log.info("Individual record with abrupt user disconnection");
|
||||
|
||||
CustomHttpClient restClient = new CustomHttpClient(OpenViduTestAppE2eTest.OPENVIDU_URL, "OPENVIDUAPP",
|
||||
OpenViduTestAppE2eTest.OPENVIDU_SECRET);
|
||||
JsonObject config = restClient.rest(HttpMethod.GET, "/openvidu/api/config", HttpURLConnection.HTTP_OK);
|
||||
|
||||
String defaultOpenViduWebhookEndpoint = null;
|
||||
Integer defaultOpenViduRecordingAutostopTimeout = null;
|
||||
if (config.has("OPENVIDU_WEBHOOK_ENDPOINT")) {
|
||||
defaultOpenViduWebhookEndpoint = config.get("OPENVIDU_WEBHOOK_ENDPOINT").getAsString();
|
||||
}
|
||||
if (config.has("OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT")) {
|
||||
defaultOpenViduRecordingAutostopTimeout = config.get("OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT").getAsInt();
|
||||
}
|
||||
|
||||
CountDownLatch initLatch = new CountDownLatch(1);
|
||||
io.openvidu.test.browsers.utils.webhook.CustomWebhook.main(new String[0], initLatch);
|
||||
|
||||
try {
|
||||
|
||||
if (!initLatch.await(30, TimeUnit.SECONDS)) {
|
||||
Assertions.fail("Timeout waiting for webhook springboot app to start");
|
||||
CustomWebhook.shutDown();
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> newConfig = Map.of("OPENVIDU_PRO_NETWORK_QUALITY", false,
|
||||
"OPENVIDU_PRO_SPEECH_TO_TEXT", "disabled", "OPENVIDU_WEBHOOK", true,
|
||||
"OPENVIDU_WEBHOOK_ENDPOINT", "http://127.0.0.1:7777/webhook",
|
||||
"OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT", 0);
|
||||
restartOpenViduServer(newConfig);
|
||||
|
||||
OpenViduTestappUser user = setupBrowserAndConnectToOpenViduTestapp("chrome");
|
||||
|
||||
final String sessionName = "AbruptStopOfIndividualRecording";
|
||||
final String recordingName = "ABRUPT_STOP_OF_INDIVIDUAL_RECORDING";
|
||||
|
||||
user.getDriver().findElement(By.id("add-user-btn")).click();
|
||||
user.getDriver().findElement(By.id("session-name-input-0")).clear();
|
||||
user.getDriver().findElement(By.id("session-name-input-0")).sendKeys(sessionName);
|
||||
user.getDriver().findElement(By.className("join-btn")).click();
|
||||
|
||||
user.getEventManager().waitUntilEventReaches("connectionCreated", 1);
|
||||
user.getEventManager().waitUntilEventReaches("accessAllowed", 1);
|
||||
user.getEventManager().waitUntilEventReaches("streamCreated", 1);
|
||||
user.getEventManager().waitUntilEventReaches("streamPlaying", 1);
|
||||
|
||||
int numberOfVideos = user.getDriver().findElements(By.tagName("video")).size();
|
||||
Assertions.assertEquals(1, numberOfVideos, "Expected 1 video but found " + numberOfVideos);
|
||||
Assertions.assertTrue(
|
||||
user.getBrowserUser().assertMediaTracks(user.getDriver().findElements(By.tagName("video")), true,
|
||||
true),
|
||||
"Video was expected to have audio and video tracks");
|
||||
|
||||
user.getDriver().findElement(By.id("session-api-btn-0")).click();
|
||||
Thread.sleep(1000);
|
||||
user.getDriver().findElement(By.id("rec-properties-btn")).click();
|
||||
Thread.sleep(500);
|
||||
|
||||
// Set recording name
|
||||
user.getDriver().findElement(By.id("recording-name-field")).sendKeys(recordingName);
|
||||
// Set OutputMode to INDIVIDUAL
|
||||
user.getDriver().findElement(By.id("rec-outputmode-select")).click();
|
||||
Thread.sleep(500);
|
||||
user.getDriver().findElement(By.id("option-INDIVIDUAL")).click();
|
||||
Thread.sleep(500);
|
||||
|
||||
user.getDriver().findElement(By.id("start-recording-btn")).click();
|
||||
|
||||
user.getWaiter().until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value",
|
||||
"Recording started [" + sessionName + "]"));
|
||||
|
||||
user.getEventManager().waitUntilEventReaches("recordingStarted", 1);
|
||||
|
||||
Thread.sleep(3000);
|
||||
|
||||
CustomWebhook.clean();
|
||||
|
||||
((ChromeUser) user.getBrowserUser()).simulateCrash();
|
||||
|
||||
JsonObject event = CustomWebhook.waitForEvent("webrtcConnectionDestroyed", 20);
|
||||
Assertions.assertEquals("networkDisconnect", event.get("reason").getAsString(),
|
||||
"Wrong reason for webrtcConnectionDestroyed event");
|
||||
|
||||
event = CustomWebhook.waitForEvent("participantLeft", 1);
|
||||
Assertions.assertEquals("networkDisconnect", event.get("reason").getAsString(),
|
||||
"Wrong reason for participantLeft event");
|
||||
|
||||
event = CustomWebhook.waitForEvent("recordingStatusChanged", 1);
|
||||
Assertions.assertEquals("lastParticipantLeft", event.get("reason").getAsString(),
|
||||
"Wrong reason for recordingStatusChanged event");
|
||||
Assertions.assertEquals("stopped", event.get("status").getAsString(),
|
||||
"Wrong status in recordingStatusChanged event");
|
||||
|
||||
event = CustomWebhook.waitForEvent("sessionDestroyed", 1);
|
||||
Assertions.assertEquals("lastParticipantLeft", event.get("reason").getAsString(),
|
||||
"Wrong reason for sessionDestroyed event");
|
||||
|
||||
event = CustomWebhook.waitForEvent("recordingStatusChanged", 1);
|
||||
Assertions.assertEquals("lastParticipantLeft", event.get("reason").getAsString(),
|
||||
"Wrong reason for recordingStatusChanged event");
|
||||
Assertions.assertEquals("ready", event.get("status").getAsString(),
|
||||
"Wrong status in recordingStatusChanged event");
|
||||
|
||||
// Check that the INDIVIDUAL recording has been properly saved and is healthy
|
||||
// even after Chrome crash
|
||||
String recPath = "/opt/openvidu/recordings/" + sessionName + "/";
|
||||
Recording recording = new OpenVidu(OpenViduTestAppE2eTest.OPENVIDU_URL,
|
||||
OpenViduTestAppE2eTest.OPENVIDU_SECRET)
|
||||
.getRecording(sessionName);
|
||||
this.recordingUtils.checkIndividualRecording(recPath, recording, 1, "opus", "vp8", true, 15);
|
||||
|
||||
} finally {
|
||||
Map<String, Object> oldConfig = new HashMap<>();
|
||||
oldConfig.put("OPENVIDU_WEBHOOK", false);
|
||||
if (defaultOpenViduWebhookEndpoint != null) {
|
||||
oldConfig.put("OPENVIDU_WEBHOOK_ENDPOINT", defaultOpenViduWebhookEndpoint);
|
||||
}
|
||||
if (defaultOpenViduRecordingAutostopTimeout != null) {
|
||||
oldConfig.put("OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT", defaultOpenViduRecordingAutostopTimeout);
|
||||
}
|
||||
restartOpenViduServer(oldConfig);
|
||||
CustomWebhook.shutDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("REST API PRO test")
|
||||
void restApiProTest() throws Exception {
|
||||
|
|
@ -1143,6 +1299,7 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@OnlyKurento
|
||||
@DisplayName("Network quality test")
|
||||
void networkQualityTest() throws Exception {
|
||||
|
||||
|
|
@ -1290,7 +1447,8 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
|
||||
filterOptionsInput = user.getDriver().findElement(By.id("filter-options-field"));
|
||||
filterOptionsInput.clear();
|
||||
filterOptionsInput.sendKeys("{\"url\": \"https://upload.wikimedia.org/wikipedia/commons/thumb/6/62/Solid_red.svg/1024px-Solid_red.svg.png\"}");
|
||||
filterOptionsInput.sendKeys(
|
||||
"{\"url\": \"https://upload.wikimedia.org/wikipedia/commons/thumb/6/62/Solid_red.svg/1024px-Solid_red.svg.png\"}");
|
||||
user.getDriver().findElement(By.id("apply-filter-btn")).click();
|
||||
user.getWaiter().until(
|
||||
ExpectedConditions.attributeContains(By.id("operation-response-text-area"), "value", "Filter applied"));
|
||||
|
|
@ -1326,7 +1484,8 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
|
||||
// Blue
|
||||
filterParamsInput.clear();
|
||||
filterParamsInput.sendKeys("{\"url\": \"https://png.pngtree.com/thumb_back/fw800/background/20210207/pngtree-blue-pure-color-simple-background-image_557085.jpg\"}");
|
||||
filterParamsInput.sendKeys(
|
||||
"{\"url\": \"https://png.pngtree.com/thumb_back/fw800/background/20210207/pngtree-blue-pure-color-simple-background-image_557085.jpg\"}");
|
||||
user.getDriver().findElement(By.id("exec-filter-btn")).click();
|
||||
user.getWaiter().until(ExpectedConditions.attributeContains(By.id("operation-response-text-area"), "value",
|
||||
"Filter method executed"));
|
||||
|
|
|
|||
|
|
@ -1646,7 +1646,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
String recPath = recordingsPath + sessionName + "/";
|
||||
|
||||
Recording recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(sessionName);
|
||||
this.recordingUtils.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true);
|
||||
this.recordingUtils.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true, 2);
|
||||
|
||||
// Try to get the stopped recording
|
||||
user.getDriver().findElement(By.id("get-recording-btn")).click();
|
||||
|
|
@ -1853,17 +1853,17 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
String recPath = recordingsPath + SESSION_NAME + "/";
|
||||
Recording recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME);
|
||||
this.recordingUtils.checkMultimediaFile(new File(recPath + recording.getName() + ".mp4"), false, true,
|
||||
recording.getDuration(), recording.getResolution(), recording.getFrameRate(), null, "h264", true);
|
||||
recording.getDuration(), recording.getResolution(), recording.getFrameRate(), null, "h264", true, 2);
|
||||
|
||||
// Check video-only INDIVIDUAL recording
|
||||
recPath = recordingsPath + SESSION_NAME + "~1/";
|
||||
recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME + "~1");
|
||||
this.recordingUtils.checkIndividualRecording(recPath, recording, 3, "opus", "vp8", true);
|
||||
this.recordingUtils.checkIndividualRecording(recPath, recording, 3, "opus", "vp8", true, 2);
|
||||
|
||||
// Check audio-only INDIVIDUAL recording
|
||||
recPath = recordingsPath + SESSION_NAME + "~2/";
|
||||
recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME + "~2");
|
||||
this.recordingUtils.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true);
|
||||
this.recordingUtils.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true, 2);
|
||||
|
||||
user.getDriver().findElement(By.id("close-dialog-btn")).click();
|
||||
Thread.sleep(500);
|
||||
|
|
@ -1945,7 +1945,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
// Check audio-only COMPOSED recording
|
||||
Recording recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME);
|
||||
this.recordingUtils.checkMultimediaFile(new File(recordingFilePath), true, false, recording.getDuration(), null,
|
||||
null, "opus", null, true);
|
||||
null, "opus", null, true, 2);
|
||||
|
||||
user.getDriver().findElement(By.id("close-dialog-btn")).click();
|
||||
Thread.sleep(500);
|
||||
|
|
@ -2970,7 +2970,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
Assertions.assertFalse(OV.fetch(), "OpenVidu.fetch() should return false");
|
||||
|
||||
this.recordingUtils.checkIndividualRecording("/opt/openvidu/recordings/" + customSessionId + "/", recording, 2,
|
||||
"opus", "vp8", false);
|
||||
"opus", "vp8", false, 2);
|
||||
|
||||
// Not recorded session
|
||||
try {
|
||||
|
|
@ -3991,7 +3991,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
Assertions.assertTrue(rec.getSize() > 0, "Recording size is 0");
|
||||
|
||||
this.recordingUtils.checkIndividualRecording("/opt/openvidu/recordings/TestSession/", rec, 1, "opus", "vp8",
|
||||
true);
|
||||
true, 10);
|
||||
|
||||
user.getDriver().findElement(By.id("remove-all-users-btn")).click();
|
||||
user.getEventManager().clearAllCurrentEvents();
|
||||
|
|
@ -4075,7 +4075,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
Assertions.assertTrue(rec.getSize() > 0, "Recording size is 0");
|
||||
|
||||
this.recordingUtils.checkIndividualRecording("/opt/openvidu/recordings/TestSession/", rec, 1, "opus", "vp8",
|
||||
true);
|
||||
true, 10);
|
||||
|
||||
OV.fetch();
|
||||
sessions = OV.getActiveSessions();
|
||||
|
|
@ -4569,7 +4569,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
CustomWebhook.waitForEvent("recordingStatusChanged", 10); // Ready
|
||||
|
||||
Recording recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(recId);
|
||||
this.recordingUtils.checkIndividualRecording(recPath + recId + "/", recording, 1, "opus", "vp8", true);
|
||||
this.recordingUtils.checkIndividualRecording(recPath + recId + "/", recording, 1, "opus", "vp8", true, 2);
|
||||
|
||||
// Test IPCAM individual recording (IPCAM video only, recording audio and video)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue