All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kurento.test.browser.WebPage Maven / Gradle / Ivy

Go to download

This project contains test cases for testing Kurento Java Client and Kurento Media Server.

There is a newer version: 6.18.0
Show newest version
/*
 * (C) Copyright 2015 Kurento (http://kurento.org/)
 *
 * 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 org.kurento.test.browser;

import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.kurento.test.grid.GridHandler;
import org.kurento.test.latency.LatencyException;
import org.kurento.test.latency.VideoTag;
import org.kurento.test.monitor.PeerConnectionStats;
import org.kurento.test.monitor.SystemMonitorManager;
import org.kurento.test.utils.Ffmpeg;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Generic web page for tests using Kurento test infrastructure.
 *
 * @author Boni Garcia ([email protected])
 * @since 5.1.0
 */
public class WebPage {

  public static Logger log = LoggerFactory.getLogger(WebPage.class);

  private Map countDownLatchEvents = new HashMap<>();
  private List callbackThreads = new ArrayList<>();

  public Browser browser;

  public Browser getBrowser() {
    return browser;
  }

  public void setBrowser(Browser browser) {
    this.browser = browser;
  }

  public void takeScreeshot(String file) throws IOException {
    File scrFile = ((TakesScreenshot) getBrowser().getWebDriver()).getScreenshotAs(OutputType.FILE);
    FileUtils.copyFile(scrFile, new File(file));
  }

  /*
   * setThresholdTime
   */
  public void setThresholdTime(int thresholdTime) {
    browser.setThresholdTime(thresholdTime);
  }

  /*
   * setColorCoordinates
   */
  public void setColorCoordinates(int x, int y) {
    browser.executeScript("kurentoTest.setColorCoordinates(" + x + "," + y + ");");
  }

  /*
   * checkColor
   */
  public void checkColor(String... videoTags) {
    String tags = "";
    for (String s : videoTags) {
      if (!tags.isEmpty()) {
        tags += ",";
      }
      tags += "'" + s + "'";
    }
    browser.executeScript("kurentoTest.checkColor(" + tags + ");");
  }

  /*
   * similarColorAt
   */
  public boolean similarColorAt(String videoTag, Color expectedColor, int x, int y) {
    setColorCoordinates(x, y);
    return similarColor(videoTag, expectedColor);

  }

  /*
   * similarColor
   */
  public boolean similarColor(String videoTag, Color expectedColor) {
    boolean out;
    final long endTimeMillis = System.currentTimeMillis() + browser.getTimeout() * 1000;

    boolean logWarn = true;
    while (true) {
      out = compareColor(videoTag, expectedColor, logWarn);
      if (out || System.currentTimeMillis() > endTimeMillis) {
        break;
      } else {
        // Polling: wait 200 ms and check again the color
        // Max wait = timeout variable
        try {
          Thread.sleep(200);
        } catch (InterruptedException e) {
          log.trace("InterruptedException in guard condition ({})", e.getMessage());
        }
      }
      logWarn = false;
    }
    return out;
  }

  /*
   * compareColor
   */
  public boolean compareColor(String videoTag, Color expectedColor, boolean logWarn) {
    @SuppressWarnings("unchecked")
    List realColor = (List) browser.executeScriptAndWaitOutput(
        "return kurentoTest.colorInfo['" + videoTag + "'].currentColor;");

    long red = realColor.get(0);
    long green = realColor.get(1);
    long blue = realColor.get(2);

    double distance = Math.sqrt((red - expectedColor.getRed()) * (red - expectedColor.getRed())
        + (green - expectedColor.getGreen()) * (green - expectedColor.getGreen())
        + (blue - expectedColor.getBlue()) * (blue - expectedColor.getBlue()));

    String expectedColorStr = "[R=" + expectedColor.getRed() + ", G=" + expectedColor.getGreen()
        + ", B=" + expectedColor.getBlue() + "]";
    String realColorStr = "[R=" + red + ", G=" + green + ", B=" + blue + "]";
    boolean out = distance <= browser.getColorDistance();
    if (!out) {
      if (logWarn) {
        log.warn("Color NOT detected in video stream. Expected: {}, Real: {}", expectedColorStr,
            realColorStr);
      }
    } else {
      log.debug("Detected color in video stream. Expected: {}, Real: {}", expectedColorStr,
          realColorStr);
    }

    return out;
  }

  /*
   * activatePeerConnectionInboundStats
   */
  public void activatePeerConnectionInboundStats(String peerConnectionId) {
    activatePeerConnectionStats("activateInboundRtcStats", peerConnectionId);
  }

  /*
   * activatePeerConnectionOutboundStats
   */
  public void activatePeerConnectionOutboundStats(String peerConnectionId) {
    activatePeerConnectionStats("activateOutboundRtcStats", peerConnectionId);
  }

  private void activatePeerConnectionStats(String jsFunction, String peerConnectionId) {

    try {
      browser.executeScript("kurentoTest." + jsFunction + "('" + peerConnectionId + "');");

    } catch (WebDriverException we) {
      we.printStackTrace();

      // If client is not ready to gather rtc statistics, we just log it
      // as warning (it is not an error itself)
      log.warn("Client does not support RTC statistics (function kurentoTest.{}() not defined)",
          jsFunction);
    }
  }

  /**
   *
   * @param peerConnectionId
   */
  public void stopPeerConnectionInboundStats(String peerConnectionId) {
    stopPeerConnectionStats("stopInboundRtcStats", peerConnectionId);
  }

  /**
   *
   * @param peerConnectionId
   */
  public void stopPeerConnectionOutboundStats(String peerConnectionId) {
    stopPeerConnectionStats("stopOutboundRtcStats", peerConnectionId);
  }

  private void stopPeerConnectionStats(String jsFunction, String peerConnectionId) {

    try {
      log.debug("kurentoTest." + jsFunction + "('" + peerConnectionId + "');");
      browser.executeScript("kurentoTest." + jsFunction + "('" + peerConnectionId + "');");

    } catch (WebDriverException we) {
      we.printStackTrace();

      // If client is not ready to gather rtc statistics, we just log it
      // as warning (it is not an error itself)
      log.warn("Client does not support RTC statistics (function kurentoTest.{}() not defined)");
    }
  }

  /*
   * getLatency
   */
  @SuppressWarnings("deprecation")
  public long getLatency() throws InterruptedException {
    final CountDownLatch latch = new CountDownLatch(1);
    final long[] out = new long[1];
    Thread t = new Thread() {
      @Override
      public void run() {
        Object latency = browser.executeScript("return kurentoTest.getLatency();");
        if (latency != null) {
          out[0] = (Long) latency;
        } else {
          out[0] = Long.MIN_VALUE;
        }
        latch.countDown();
      }
    };
    t.start();
    if (!latch.await(browser.getTimeout(), TimeUnit.SECONDS)) {
      t.interrupt();
      t.stop();
      throw new LatencyException("Timeout getting latency (" + browser.getTimeout() + "  seconds)");
    }
    return out[0];
  }

  public void waitColor(long timeoutSeconds, final VideoTag videoTag, final Color color) {
    WebDriverWait wait = new WebDriverWait(browser.getWebDriver(), timeoutSeconds);
    wait.until(new ExpectedCondition() {
      @Override
      public Boolean apply(WebDriver d) {
        return !((JavascriptExecutor) d).executeScript(videoTag.getColor()).equals(color);
      }
    });
  }

  /*
   * getCurrentTime
   */
  public long getCurrentTime(VideoTag videoTag) {
    Object time = browser.executeScript(videoTag.getTime());
    return time == null ? 0 : (Long) time;
  }

  /*
   * getCurrentColor
   */
  @SuppressWarnings("unchecked")
  public Color getCurrentColor(VideoTag videoTag) {
    return getColor((List) browser.executeScript(videoTag.getColor()));
  }

  private Color getColor(List color) {
    return new Color(color.get(0).intValue(), color.get(1).intValue(), color.get(2).intValue());
  }

  /*
   * checkLatencyUntil
   */
  public void checkLatencyUntil(SystemMonitorManager monitor, long endTimeMillis)
      throws InterruptedException, IOException {
    while (true) {
      if (System.currentTimeMillis() > endTimeMillis) {
        break;
      }
      Thread.sleep(100);
      try {
        long latency = getLatency();
        if (latency != Long.MIN_VALUE) {
          monitor.addCurrentLatency(latency);
        }
      } catch (LatencyException le) {
        monitor.incrementLatencyErrors();
      }
    }
  }

  /*
   * getRtcStats
   */
  @SuppressWarnings("unchecked")
  public PeerConnectionStats getRtcStats() {
    Map out = new HashMap<>();
    try {
      if (browser != null && browser.getWebDriver() != null) {
        out = (Map) browser.executeScript("return kurentoTest.rtcStats;");

        log.debug(">>>>>>>>>> kurentoTest.rtcStats {} {}", browser.getId(), out);
      }
    } catch (WebDriverException we) {
      // If client is not ready to gather rtc statistics, we just log it
      // as warning (it is not an error itself)
      log.warn("Client does not support RTC statistics" + " (variable rtcStats is not defined)");
    }
    return new PeerConnectionStats(out);
  }

  /*
   * activateLatencyControl
   */
  public void activateLatencyControl(String localId, String remoteId) {
    browser.executeScript(
        "kurentoTest.activateLatencyControl('" + localId + "', '" + remoteId + "');");

  }

  /*
   * getTimeout
   */
  public int getTimeout() {
    return browser.getTimeout();
  }

  /*
   * setTimeout
   */
  public void setTimeout(int timeoutSeconds) {
    browser.changeTimeout(timeoutSeconds);
  }

  /*
   * getThresholdTime
   */
  public int getThresholdTime() {
    return browser.getThresholdTime();

  }

  /*
   * equalDataChannelMessage
   */
  public boolean compareDataChannelMessage(String message) {
    boolean out;
    final long endTimeMillis = System.currentTimeMillis() + browser.getTimeout() * 1000;
    while (true) {
      String messageReceived = (String) browser
          .executeScript("return kurentoTest.getDataChannelMessage()");
      out = (message.equals(messageReceived));
      if (out || System.currentTimeMillis() > endTimeMillis) {
        break;
      } else {
        // Polling: wait 200 ms and check again the color
        // Max wait = timeout variable
        try {
          Thread.sleep(200);
        } catch (InterruptedException e) {
          log.trace("InterruptedException in guard condition ({})", e.getMessage());
        }
      }
    }
    return out;
  }

  /*
   * syncTimeForOcr
   */
  public void syncTimeForOcr(String videoTagId, String peerConnectionId) {
    browser.executeScript(
        "kurentoTest.syncTimeForOcr('" + videoTagId + "', '" + peerConnectionId + "');");

    log.debug("Sync time in {} {}", browser.getId(), videoTagId);
    WebDriverWait wait = new WebDriverWait(browser.getWebDriver(), browser.getTimeout());
    wait.until(new ExpectedCondition() {
      @Override
      public Boolean apply(WebDriver d) {
        return (Boolean) ((JavascriptExecutor) d).executeScript("return kurentoTest.sync;");
      }
    });
    log.debug("[Done] Sync time in {} {}", browser.getId(), videoTagId);
  }

  /*
   * startOcr
   */
  public void startOcr() {
    browser.executeScript("kurentoTest.startOcr();");
  }

  /*
   * endOcr
   */
  public void endOcr() {
    browser.executeScript("kurentoTest.endOcr();");
  }

  /*
   * getOcrMap
   */
  @SuppressWarnings("unchecked")
  public Map> getOcrMap() {
    Map> ocrMap = (Map>) browser
        .executeScript("return kurentoTest.ocrMap;");

    // Transform data structure to serializable (because
    // com.google.common.collect.Maps$TransformedEntriesMap is not)
    Map> serializableMap = new TreeMap>();
    for (String key : ocrMap.keySet()) {
      serializableMap.put(key, new HashMap(ocrMap.get(key)));
    }

    return serializableMap;
  }

  public void startRecording(String stream) {
    browser.executeScript("kurentoTest.startRecording(" + stream + ");");
  }

  public void stopRecording() {
    browser.executeScript("kurentoTest.stopRecording();");
    getProperty("recordRTC");
  }

  public File saveRecordingToDisk(String fileName, String downloadsFolder) {
    browser.executeScript("kurentoTest.saveRecordingToDisk('" + fileName + "');");
    File output = new File(downloadsFolder + fileName);
    do {
      if (!output.exists()) {
        try {
          Thread.sleep(500); // polling
        } catch (InterruptedException e) {
          log.warn("Exception waiting for file {}", output, e);
        }
      } else {
        break;
      }
    } while (true);
    return output;
  }

  public void openRecordingInNewTab() {
    browser.executeScript("kurentoTest.openRecordingInNewTab();");
  }

  public File getRecording() throws IOException {
    File tmpFile = File.createTempFile(String.valueOf(System.nanoTime()), ".webm");
    return getRecording(tmpFile.getAbsolutePath());
  }

  public File getRecording(String fileName) throws IOException {
    browser.executeScript("kurentoTest.recordingToData();");
    String recording = getProperty("recordingData").toString();

    // Base64 to File
    File outputFile = new File(fileName);
    byte[] bytes = Base64.decodeBase64(recording.substring(recording.lastIndexOf(",") + 1));
    FileUtils.writeByteArrayToFile(outputFile, bytes);

    return outputFile;
  }

  private Object getProperty(String property) {
    Object value = null;
    final int pollTimeMs = 200;
    for (int i = 0; i < 60; i++) {
      value = browser.executeScript("return kurentoTest." + property + ";");
      if (value != null) {
        break;
      } else {
        try {
          log.debug("{} not present still... waiting {} ms", property, pollTimeMs);
          Thread.sleep(pollTimeMs);
        } catch (InterruptedException e) {
          log.warn("Exception wait polling whil getting {}", property, e);
        }
      }
    }

    String clazz = value != null ? value.getClass().getName() : "";
    log.trace(">>> getProperty {} {} {}", property, value, clazz);
    return value;
  }

  public void subscribeEvent(final String videoTag, final String eventType) {
    subscribeEventsToVideoTag("document.getElementById('" + videoTag + "')", eventType);
  }

  /*
   * subscribeEventsToVideoTag
   */
  protected void subscribeEventsToVideoTag(final String videoTag, final String eventType) {
    CountDownLatch latch = new CountDownLatch(1);

    final String browserName = browser.getId();
    log.debug("Subscribe event '{}' in browser '{}'", eventType, browserName);

    countDownLatchEvents.put(browserName + eventType, latch);
    addEventListener(videoTag, eventType, new BrowserEventListener() {
      @Override
      public void onEvent(String event) {
        consoleLog(ConsoleLogLevel.INFO, "Event in " + videoTag + " tag: " + event);
        countDownLatchEvents.get(browserName + eventType).countDown();
      }
    });
  }

  /*
   * waitForEvent
   */
  public boolean waitForEvent(final String eventType) throws InterruptedException {

    String browserName = browser.getId();
    log.debug("Waiting for event '{}' in browser '{}'", eventType, browserName);

    if (!countDownLatchEvents.containsKey(browserName + eventType)) {
      log.error("We cannot wait for an event without previous subscription");
      return false;
    }

    boolean result = countDownLatchEvents.get(browserName + eventType).await(browser.getTimeout(),
        TimeUnit.SECONDS);

    // Record local audio when playing event reaches the browser
    if (eventType.equalsIgnoreCase("playing") && browser.getRecordAudio() > 0) {
      if (browser.isRemote()) {
        Ffmpeg.recordRemote(GridHandler.getInstance().getNode(browser.getId()),
            browser.getRecordAudio(), browser.getAudioSampleRate(), browser.getAudioChannel());
      } else {
        Ffmpeg.record(browser.getRecordAudio(), browser.getAudioSampleRate(),
            browser.getAudioChannel());
      }
    }

    countDownLatchEvents.remove(browserName + eventType);
    return result;
  }

  /*
   * addEventListener
   */
  @SuppressWarnings("deprecation")
  public void addEventListener(final String videoTag, final String eventType,
      final BrowserEventListener eventListener) {
    Thread t = new Thread() {
      @Override
      public void run() {
        browser.executeScript(
            videoTag + ".addEventListener('" + eventType + "', kurentoTest.videoEvent, false);");
        try {
          new WebDriverWait(browser.getWebDriver(), browser.getTimeout())
              .until(new ExpectedCondition() {
                @Override
                public Boolean apply(WebDriver d) {
                  Object videoEventValue = ((JavascriptExecutor) d)
                      .executeScript("return kurentoTest.videoEventValue;");
                  return videoEventValue != null
                      && videoEventValue.toString().equalsIgnoreCase(eventType);
                }
              });
          eventListener.onEvent(eventType);
        } catch (Throwable t) {
          log.error("~~~ Exception in addEventListener {}", t.getMessage());
          t.printStackTrace();
          this.interrupt();
          this.stop();
        }
      }
    };
    callbackThreads.add(t);
    t.setDaemon(true);
    t.start();
  }

  /*
   * consoleLog
   */
  public void consoleLog(ConsoleLogLevel level, String message) {
    message = message.replaceAll("'", "\"");
    browser.executeScript("console." + level.toString() + "('" + message + "');");
  }

  @After
  @SuppressWarnings("deprecation")
  public void teardownKurentoServices() throws Exception {
    for (Thread t : callbackThreads) {
      t.stop();
    }
  }

  /*
   * stopWebRtc
   */
  public void stopWebRtc() {
    browser.executeScript("stop();");
    browser.executeScript("var kurentoTest = new KurentoTest();");
    countDownLatchEvents.clear();
  }

  /*
   * close
   */
  public void close() {
    browser.close();
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy