openvidu-2/openvidu-test/src/main/java/io/openvidu/test/RoomClientFakeTest.java
2019-01-21 21:32:17 +01:00

351 lines
12 KiB
Java

/*
* (C) Copyright 2017-2019 OpenVidu (https://openvidu.io/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.openvidu.test;
import static org.kurento.commons.PropertiesManager.getProperty;
import static org.kurento.test.config.TestConfiguration.TEST_HOST_PROPERTY;
import static org.kurento.test.config.TestConfiguration.TEST_PORT_PROPERTY;
import static org.kurento.test.config.TestConfiguration.TEST_PROTOCOL_DEFAULT;
import static org.kurento.test.config.TestConfiguration.TEST_PROTOCOL_PROPERTY;
import static org.kurento.test.config.TestConfiguration.TEST_PUBLIC_IP_DEFAULT;
import static org.kurento.test.config.TestConfiguration.TEST_PUBLIC_IP_PROPERTY;
import static org.kurento.test.config.TestConfiguration.TEST_PUBLIC_PORT_PROPERTY;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.kurento.client.KurentoClient;
import org.kurento.commons.PropertiesManager;
import org.kurento.commons.exception.KurentoException;
import org.kurento.test.base.KurentoTest;
import org.kurento.test.browser.WebPage;
import org.kurento.test.config.BrowserScope;
import org.kurento.test.config.Protocol;
import org.kurento.test.docker.Docker;
import org.kurento.test.services.FakeKmsService;
import org.kurento.test.services.KmsService;
import org.kurento.test.services.Service;
import org.kurento.test.services.WebServerService;
import org.kurento.test.utils.Shell;
import io.openvidu.test.fake.util.FakeSession;
/**
* Base for Kurento Room tests with browsers and fake clients.
*
* @author Radu Tom Vlad (rvlad@naevatec.com)
* @since 6.2.1
*/
public abstract class RoomClientFakeTest<W extends WebPage> extends OpenViduClientBrowserTest<W> {
public static @Service(3) KmsService fakeKms = new FakeKmsService();
public static String testFiles = KurentoTest.getTestFilesDiskPath();
// overwritten if running in Docker
public static String serverAddress = PropertiesManager.getProperty(TEST_HOST_PROPERTY,
getProperty(TEST_PUBLIC_IP_PROPERTY, TEST_PUBLIC_IP_DEFAULT));
public static int serverPort = getProperty(TEST_PORT_PROPERTY,
getProperty(TEST_PUBLIC_PORT_PROPERTY, WebServerService.getAppHttpsPort()));
public static Protocol protocol = Protocol.valueOf(getProperty(TEST_PROTOCOL_PROPERTY,
TEST_PROTOCOL_DEFAULT).toUpperCase());
// might get overwritten by custom room apps
public static String appWsPath = "/openvidu";
public long JOIN_ROOM_TOTAL_TIMEOUT_IN_SECONDS = 30;
public long ACTIVE_LIVE_TOTAL_TIMEOUT_IN_SECONDS = 180;
public long ROOM_ACTIVITY_IN_SECONDS = 30;
public long LEAVE_ROOM_TOTAL_TIMEOUT_IN_SECONDS = 10;
public URI appWsUrl;
public URI appBaseUrl;
public URI appUrl;
public ConcurrentMap<String, FakeSession> sessions = new ConcurrentHashMap<String, FakeSession>();
public KurentoClient fakeKurentoClient;
public RoomClientFakeTest() {
super();
}
@Override
public void setupBrowserTest() throws InterruptedException {
super.setupBrowserTest();
calculateUrl();
String wsProtocol = "ws";
if (protocol.equals(Protocol.HTTPS)) {
wsProtocol = "wss";
}
String hostName = appUrl.getHost();
try {
appWsUrl = new URI(wsProtocol, null, hostName, serverPort, appWsPath, null, null);
} catch (URISyntaxException e) {
throw new KurentoException("Exception generating WS URI from " + wsProtocol + ", " + hostName
+ ", server port " + serverPort + " and WS path " + appWsPath);
}
log.debug("Protocol: {}, Hostname: {}, Port: {}, Path: {}", wsProtocol, hostName, serverPort,
appWsPath);
fakeKurentoClient = fakeKms.getKurentoClient();
Assert.assertNotNull("Fake Kurento Client is null", fakeKurentoClient);
}
@Override
public void teardownBrowserTest() {
for (FakeSession s : sessions.values()) {
try {
s.close();
} catch (IOException e) {
log.warn("Error closing session", e);
}
}
fakeKms.closeKurentoClient();
super.teardownBrowserTest();
}
public void calculateUrl() {
if (appUrl == null) {
String hostName = serverAddress;
if (BrowserScope.DOCKER.equals(testScope)) {
Docker docker = Docker.getSingleton();
if (docker.isRunningInContainer()) {
hostName = docker.getContainerIpAddress();
} else {
hostName = docker.getHostIpForContainers();
}
}
log.debug("Protocol: {}, Hostname: {}, Port: {}, Web page type: {}", protocol, hostName,
serverPort, webPageType);
try {
appUrl = new URI(protocol.toString(), null, hostName, serverPort, webPageType.toString(),
null, null);
} catch (URISyntaxException e) {
throw new KurentoException("Exception generating URI from " + protocol + ", " + hostName
+ ", server port " + serverPort + " and webpage type " + webPageType);
}
try {
appBaseUrl = new URI(protocol.toString(), null, hostName, serverPort, null, null, null);
} catch (URISyntaxException e) {
throw new KurentoException("Exception generating URI from " + protocol + ", " + hostName
+ ", server port " + serverPort);
}
}
}
public FakeSession getSession(String room) {
return sessions.get(room);
}
public FakeSession createSession(String room) {
if (sessions.containsKey(room)) {
return sessions.get(room);
}
FakeSession s = new FakeSession(appWsUrl.toString(), room, fakeKurentoClient);
FakeSession old = sessions.putIfAbsent(room, s);
if (old != null) {
return old;
}
return s;
}
public void closeSession(String room) {
FakeSession session = sessions.get(room);
if (session != null) {
try {
session.close();
} catch (IOException e) {
log.warn("Error closing session", e);
}
}
}
public FakeSession removeSession(String room) {
return sessions.remove(room);
}
public CountDownLatch parallelJoinFakeUsers(final List<String> relativePaths, final String room,
final KurentoClient kurento) {
if (relativePaths == null || relativePaths.isEmpty()) {
execExceptions.put("parallelJoinFakeUsers-" + room, new Exception(
"Null or empty play paths list"));
return null;
}
int userThreads = relativePaths.size();
log.info("Joining room '{}': {} fake users with relative play paths:\n{}", room, userThreads,
relativePaths);
final CountDownLatch joinLatch = new CountDownLatch(userThreads);
parallelTasks(userThreads, USER_FAKE_PREFIX, "parallelJoinFakeUsers-" + room, new Task() {
@Override
public void exec(int numTask) throws Exception {
try {
String userName = getFakeKey(numTask);
FakeSession s = createSession(room);
String fullPlayPath = getPlaySourcePath(userName, relativePaths.get(numTask));
if (kurento == null) {
s.newParticipant(userName, fullPlayPath, true, true);
} else {
s.newParticipant(userName, fullPlayPath, true, true, kurento);
}
} finally {
joinLatch.countDown();
}
}
});
return joinLatch;
}
protected CountDownLatch parallelLeaveFakeUsers(final String room, int userThreads) {
final CountDownLatch leaveLatch = new CountDownLatch(userThreads);
parallelTasks(userThreads, USER_FAKE_PREFIX, "parallelLeaveFakeUsers-" + room, new Task() {
@Override
public void exec(int numTask) throws Exception {
try {
getSession(room).getParticipant(getFakeKey(numTask)).leaveRoom();
} finally {
leaveLatch.countDown();
}
}
});
return leaveLatch;
}
protected CountDownLatch parallelWaitActiveLive(final String room, int userThreads) {
final CountDownLatch waitForLatch = new CountDownLatch(userThreads);
parallelTasks(userThreads, USER_FAKE_PREFIX, "parallelWaitForActiveLive-" + room, new Task() {
@Override
public void exec(int numTask) throws Exception {
getSession(room).getParticipant(getFakeKey(numTask)).waitForActiveLive(waitForLatch);
}
});
return waitForLatch;
}
public void await(CountDownLatch waitLatch, long actionTimeoutInSeconds, String action,
int userThreads) {
failWithExceptions();
try {
if (!waitLatch.await(actionTimeoutInSeconds, TimeUnit.SECONDS)) {
execExceptions.put(action, new Exception("Timeout waiting for '" + action + "' of "
+ userThreads + " tasks (max " + actionTimeoutInSeconds + "s)"));
} else {
log.debug("Finished waiting for {}", action);
}
} catch (InterruptedException e) {
log.warn("Interrupted when waiting for {} of {} tasks (max {}s)", action, userThreads,
actionTimeoutInSeconds, e);
}
}
public void idlePeriod() {
idlePeriod("ACTIVE_LIVE", "LEAVE_ROOM", roomName);
}
public void idlePeriod(String room) {
idlePeriod("ACTIVE_LIVE", "LEAVE_ROOM", room);
}
public void idlePeriod(String previousAction, String nextAction, String room) {
failWithExceptions();
log.info("\n-----------------\n" + "Wait for {} concluded in '{}'" + "\n-----------------\n"
+ "Waiting {} seconds", previousAction, room, ROOM_ACTIVITY_IN_SECONDS);
sleep(ROOM_ACTIVITY_IN_SECONDS);
log.info("\n-----------------\n" + "{} in '{}'" + "\n-----------------\n", nextAction, room);
}
public static String getPlaySourcePath(String userName, String relativePath) throws Exception {
return getPlaySourcePath(userName, relativePath, testFiles);
}
public static String getPlaySourcePath(String userName, String relativePath, String basePath)
throws Exception {
if (relativePath == null) {
throw new Exception("Null play path for user " + userName);
}
if (!basePath.startsWith("http://") && !basePath.startsWith("https://")
&& !basePath.startsWith("file://")) {
basePath = "file://" + basePath;
}
URI playerUri = null;
try {
playerUri = new URI(basePath + relativePath);
} catch (URISyntaxException e) {
throw new Exception("Unable to construct player URI for user " + userName
+ " from base path " + basePath + " and file " + relativePath);
}
String fullPlayPath = playerUri.toString();
log.debug("Fake user '{}': using play URI {}", userName, fullPlayPath);
return fullPlayPath;
}
// use fake.kms.ws.uri instead
@Deprecated
public static String getFreePort(String wsUri) {
if (BrowserScope.DOCKER.equals(testScope)) {
log.info("Test is dockerized, returning the same WS for the KMS: {}", wsUri);
return wsUri;
}
URI url;
try {
url = new URI(wsUri);
} catch (URISyntaxException e) {
log.warn("WebSocket URI {} is malformed: " + e.getMessage(), wsUri);
throw new KurentoException("WebSocket URI " + wsUri + " is malformed");
}
int c = 0;
do {
try {
c++;
int newPort = url.getPort() + c * c * c;
url = new URI(url.getScheme(), null, url.getHost(), newPort, url.getPath(), null, null);
String updatedWsUri = url.toString();
log.debug("try#{} Is port available for running (fake) KMS using this URI? {}", c,
updatedWsUri);
String result = Shell.runAndWait("/bin/bash", "-c",
"nc -z " + url.getHost() + " " + url.getPort() + "; echo $?");
if (result.trim().equals("0")) {
log.warn("Port " + url.getPort()
+ " is used. Maybe another KMS instance is running in this port");
} else {
log.debug("URI is available: {}", updatedWsUri);
return updatedWsUri;
}
} catch (URISyntaxException e) {
log.warn("WebSocket URI {} is malformed: " + e.getMessage(), wsUri);
}
} while (c < 3);
log.warn("Giving up, will return the original URI: {}", wsUri);
return wsUri;
}
}