diff --git a/scripts/malleus.sh b/scripts/malleus.sh index 00b12c95f..510ebdd4e 100755 --- a/scripts/malleus.sh +++ b/scripts/malleus.sh @@ -5,7 +5,7 @@ if [ -n "$DEBUG" ]; then fi usage() { - echo "Usage: $0 [--conferences=MALLEUS_CONFERENCES] [--participants=MALLEUS_PARTICIPANTS] [--senders=MALLEUS_SENDERS] [--audio-senders=MALLEUS_AUDIO_SENDERS] [--senders-per-tab=MALLEUS_SENDERS_PER_NODE] [--receivers-per-tab=MALLEUS_RECEIVERS_PER_NODE] [--senders-per-node=MALLEUS_SENDERS_PER_NODE] [--receivers-per-node=MALLEUS_RECEIVERS_PER_NODE] [--duration=MALLEUS_DURATION (s)] [--join-delay=MALLEUS_JOIN_DELAY (ms)] [--room-name-prefix=MALLEUS_ROOM_NAME_PREFIX] [--hub-url=MALLEUS_HUB_URL] [--instance-url=MALLEUS_INSTANCE_URL] [--regions=MALLEUS_REGIONS] [--use-node-types] [--use-load-test] [--use-lite-mode] [--max-disrupted-bridges-pct=MALLEUS_MAX_DISRUPTED_BRIDGES_PCT] [--extra-sender-params=EXTRA_SENDER_PARAMS] [--extra-receiver-params=EXTRA_RECEIVER_PARAMS] [--debug] [--switch-speakers] [--use-stage-view] [--headless]" >&2 + echo "Usage: $0 [--conferences=MALLEUS_CONFERENCES] [--participants=MALLEUS_PARTICIPANTS] [--senders=MALLEUS_SENDERS] [--audio-senders=MALLEUS_AUDIO_SENDERS] [--senders-per-tab=MALLEUS_SENDERS_PER_NODE] [--receivers-per-tab=MALLEUS_RECEIVERS_PER_NODE] [--tabs-per-node=MALLEUS_TABS_PER_NODE] [--senders-per-node=MALLEUS_SENDERS_PER_NODE] [--receivers-per-node=MALLEUS_RECEIVERS_PER_NODE] [--duration=MALLEUS_DURATION (s)] [--join-delay=MALLEUS_JOIN_DELAY (ms)] [--room-name-prefix=MALLEUS_ROOM_NAME_PREFIX] [--hub-url=MALLEUS_HUB_URL] [--instance-url=MALLEUS_INSTANCE_URL] [--regions=MALLEUS_REGIONS] [--use-node-types] [--use-load-test] [--use-lite-mode] [--max-disrupted-bridges-pct=MALLEUS_MAX_DISRUPTED_BRIDGES_PCT] [--extra-sender-params=EXTRA_SENDER_PARAMS] [--extra-receiver-params=EXTRA_RECEIVER_PARAMS] [--debug] [--switch-speakers] [--use-stage-view] [--headless]" >&2 exit 1 } @@ -46,6 +46,10 @@ set_defaults() { MALLEUS_RECEIVERS_PER_NODE=1 fi + if [ -z "$MALLEUS_TABS_PER_NODE" ]; then + MALLEUS_TABS_PER_NODE=1 + fi + if [ -z "$MALLEUS_DURATION" ]; then MALLEUS_DURATION=60 fi @@ -113,6 +117,7 @@ case $1 in --receivers-per-tab) MALLEUS_RECEIVERS_PER_TAB=$optvalue;; --senders-per-node) MALLEUS_SENDERS_PER_NODE=$optvalue; MALLEUS_USE_NODE_TYPES=true;; --receivers-per-node) MALLEUS_RECEIVERS_PER_NODE=$optvalue; MALLEUS_USE_NODE_TYPES=true;; + --tabs-per-node) MALLEUS_TABS_PER_NODE=$optvalue; MALLEUS_TABS_PER_NODE=$optvalue;; --duration) MALLEUS_DURATION=$optvalue;; --join-delay) MALLEUS_JOIN_DELAY=$optvalue;; --room-name-prefix) MALLEUS_ROOM_NAME_PREFIX=$optvalue;; @@ -203,6 +208,7 @@ mvn \ -Dorg.jitsi.malleus.use_node_types=$MALLEUS_USE_NODE_TYPES \ -Dorg.jitsi.malleus.senders_per_tab=$MALLEUS_SENDERS_PER_TAB \ -Dorg.jitsi.malleus.receivers_per_tab=$MALLEUS_RECEIVERS_PER_TAB \ +-Dorg.jitsi.malleus.tabs_per_node=$MALLEUS_TABS_PER_NODE \ -Dorg.jitsi.meet.test.util.blip_script=$MALLEUS_BLIP_SCRIPT \ -Dorg.jitsi.malleus.use_load_test=$MALLEUS_USE_LOAD_TEST \ -Dorg.jitsi.malleus.use_lite_mode=$MALLEUS_USE_LITE_MODE \ diff --git a/src/test/java/org/jitsi/meet/test/MalleusJitsificus.java b/src/test/java/org/jitsi/meet/test/MalleusJitsificus.java index 418f50e00..4c64d056d 100644 --- a/src/test/java/org/jitsi/meet/test/MalleusJitsificus.java +++ b/src/test/java/org/jitsi/meet/test/MalleusJitsificus.java @@ -18,6 +18,7 @@ import org.jitsi.meet.test.base.*; import org.jitsi.meet.test.util.*; import org.jitsi.meet.test.web.*; +import org.openqa.selenium.*; import org.testng.*; import org.testng.annotations.*; @@ -73,6 +74,7 @@ public class MalleusJitsificus = "org.jitsi.malleus.senders_per_tab"; public static final String RECEIVERS_PER_TAB = "org.jitsi.malleus.receivers_per_tab"; + public static final String TABS_PER_NODE = "org.jitsi.malleus.tabs_per_node"; public static final String EXTRA_SENDER_PARAMS = "org.jitsi.malleus.extra_sender_params"; public static final String EXTRA_RECEIVER_PARAMS @@ -121,6 +123,10 @@ public Object[][] createData(ITestContext context) ? 1 : Integer.parseInt(receiversPerTabStr); + // cannot be used with switch speakers or disturb bridges + String tabsPerNodeStr = System.getProperty(TABS_PER_NODE); + int tabsPerNode = tabsPerNodeStr == null ? 1: Integer.parseInt(tabsPerNodeStr); + int durationMs = 1000 * Integer.parseInt(System.getProperty(DURATION_PNAME)); int joinDelayMs = Integer.parseInt(System.getProperty(JOIN_DELAY_PNAME)); @@ -207,7 +213,8 @@ public Object[][] createData(ITestContext context) switchSpeakers, sendersPerTab, receiversPerTab, extraSenderParams, extraReceiverParams, - useLiteMode + useLiteMode, + tabsPerNode }; } @@ -221,7 +228,8 @@ public void testMain( String[] regions, float blipMaxDisruptedPct, boolean switchSpeakers, int sendersPerTab, int receiversPerTab, String extraSenderParams, String extraReceiverParams, - boolean useLiteMode) + boolean useLiteMode, + int numberOfTabsPerNode) throws Exception { List malleusTasks = new ArrayList<>(numberOfParticipants); @@ -259,7 +267,7 @@ public void testMain( else { urlCopy.appendConfig(extraReceiverParams); - numClients = receiversPerTab; + numClients = receiversPerTab*numberOfTabsPerNode; if (useLiteMode) { @@ -287,7 +295,8 @@ public void testMain( switchSpeakers || !audioSender /* no audio */, regions == null ? null : regions[i % regions.length], numClients, - disruptBridges + disruptBridges, + numberOfTabsPerNode ); malleusTasks.add(task); task.start(pool); @@ -377,23 +386,32 @@ private class MalleusTask private Future complete; private ScheduledFuture checking; - WebParticipant participant; + List taskParticipants = new ArrayList<>(); + private String bridge; + private int numberOfTabs; + private int numClients; + + private long clientInterval; + private ScheduledExecutorService pool; public MalleusTask( int i, JitsiMeetUrl url, long durationMs, long joinDelayMs, long totalJoinDelayMs, boolean muteVideo, boolean muteAudio, String region, int numClients, - boolean enableFailureDetection) + boolean enableFailureDetection, int numberOfTabs) { this.i = i; this._url = url; this.durationMs = durationMs; + this.clientInterval = joinDelayMs; this.joinDelayMs = totalJoinDelayMs; this.muteVideo = muteVideo; this.muteAudio = muteAudio; this.enableFailureDetection = enableFailureDetection; + this.numberOfTabs = numberOfTabs; + this.numClients = numClients; if (muteVideo) { @@ -405,7 +423,6 @@ public MalleusTask( } if (numClients != 1) { - _url.appendConfig("numClients=" + numClients); _url.appendConfig("clientInterval=" + joinDelayMs); } @@ -456,11 +473,45 @@ private void join() } } - participant = participants.createParticipant("web.participant" + (i + 1), ops); - allHungUp.register(); try { - participant.joinConference(_url); + + int clientPerTab = numClients/numberOfTabs; + int rem = numClients%numberOfTabs; + + for(int i = 0; i < numberOfTabs; i++) + { + int currentClients = clientPerTab; + if (rem > 0) + { + currentClients += rem; + rem = 0; + } + int participantCount = (this.i + 1) + (i * clientPerTab); + WebParticipant participant = participants.createParticipant( + "web.participant" + participantCount, ops); + WebDriver driver = participant.getDriver(); + + // to re-use the same driver/chrome instance + ops.setDriver(driver); + + JitsiMeetUrl u = _url.copy().appendConfig("numClients=" + currentClients); + participant.joinConference(u); + + // let's create new tab and switch to it, so we prepare for the next execution + // skips the last one + if (i < numberOfTabs - 1) + { + // wait, give time for thos in the tab to join + TestUtils.waitMillis(currentClients * this.clientInterval); + ((JavascriptExecutor) driver).executeScript("window.open()"); + ArrayList tabs = new ArrayList<>(driver.getWindowHandles()); + driver.switchTo().window(tabs.get(i + 1)); + } + // the index should be also the tab index + taskParticipants.add(participant); + } + allHungUp.register(); } catch (Exception e) { @@ -476,8 +527,16 @@ private void join() { if (enableFailureDetection) { - bridge = participant.getBridgeIp(); - bridgeSelection.add(bridge); + for(int i =0; i < taskParticipants.size(); i++) + { + WebParticipant participant = taskParticipants.get(i); + WebDriver driver = participant.getDriver(); + ArrayList tabs = new ArrayList<> (driver.getWindowHandles()); + driver.switchTo().window(tabs.get(i)); + + bridge = participant.getBridgeIp(); + bridgeSelection.add(bridge); + } } } catch (Exception e) @@ -507,28 +566,38 @@ private void finish() checking.cancel(true); } - try - { - participant.hangUp(); - } - catch (Exception e) - { - TestUtils.print("Exception hanging up " + participant.getName()); - e.printStackTrace(); - } - try - { - /* There seems to be a Selenium or chrome webdriver bug where closing one parallel - * Chrome session can cause another one to close too. So wait for all sessions - * to hang up before we close any of them. - */ - allHungUp.arriveAndAwaitAdvance(); - MalleusJitsificus.this.closeParticipant(participant); - } - catch (Exception e) + // we need to start closing from the last one + for(int i = taskParticipants.size() - 1; i >= 0; i--) { - TestUtils.print("Exception closing " + participant.getName()); - e.printStackTrace(); + WebParticipant participant = taskParticipants.get(i); + WebDriver driver = participant.getDriver(); + ArrayList tabs = new ArrayList<> (driver.getWindowHandles()); + driver.switchTo().window(tabs.get(i)); + + try + { + + participant.hangUp(); + } + catch (Exception e) + { + TestUtils.print("Exception hanging up " + participant.getName()); + e.printStackTrace(); + } + try + { + /* There seems to be a Selenium or chrome webdriver bug where closing one parallel + * Chrome session can cause another one to close too. So wait for all sessions + * to hang up before we close any of them. + */ + allHungUp.arriveAndAwaitAdvance(); + MalleusJitsificus.this.closeParticipant(participant); + } + catch (Exception e) + { + TestUtils.print("Exception closing " + participant.getName()); + e.printStackTrace(); + } } } @@ -540,22 +609,30 @@ public void waitUntilComplete() throws ExecutionException, InterruptedException private void check() { - try + for(int i =0; i < taskParticipants.size(); i++) { - participant.waitForIceConnected(0 /* no timeout */); - } - catch (Exception ex) - { - TestUtils.print("Participant " + i + " is NOT connected."); - if (!bridgesToFail.contains(bridge)) + WebParticipant participant = taskParticipants.get(i); + WebDriver driver = participant.getDriver(); + ArrayList tabs = new ArrayList<>(driver.getWindowHandles()); + driver.switchTo().window(tabs.get(i)); + + try { - throw ex; + participant.waitForIceConnected(0 /* no timeout */); } - else + catch (Exception ex) { - // wait for reconnect - participant.waitForIceConnected(20); - TestUtils.print("Participant " + i + " reconnected."); + TestUtils.print("Participant " + i + " is NOT connected."); + if (!bridgesToFail.contains(bridge)) + { + throw ex; + } + else + { + // wait for reconnect + participant.waitForIceConnected(20); + TestUtils.print("Participant " + i + " reconnected."); + } } } } @@ -567,18 +644,26 @@ public void muteAudio(boolean mute) private void doMuteAudio(boolean mute) { - if (mute != muteAudio) + for(int i =0; i < taskParticipants.size(); i++) { - participant.muteAudio(mute); - muteAudio = mute; - if (mute) - { - TestUtils.print("Muted participant " + i); - } - else + WebParticipant participant = taskParticipants.get(i); + WebDriver driver = participant.getDriver(); + ArrayList tabs = new ArrayList<>(driver.getWindowHandles()); + driver.switchTo().window(tabs.get(i)); + + if (mute != muteAudio) { - TestUtils.print("Unmuted participant " + i); - spoken = true; + participant.muteAudio(mute); + muteAudio = mute; + if (mute) + { + TestUtils.print("Muted participant " + i); + } + else + { + TestUtils.print("Unmuted participant " + i); + spoken = true; + } } } } diff --git a/src/test/java/org/jitsi/meet/test/base/Participant.java b/src/test/java/org/jitsi/meet/test/base/Participant.java index f23c15d0c..9a9994995 100644 --- a/src/test/java/org/jitsi/meet/test/base/Participant.java +++ b/src/test/java/org/jitsi/meet/test/base/Participant.java @@ -208,7 +208,16 @@ public void close() cancelKeepAlive(); - driver.quit(); + ArrayList tabs = new ArrayList<> (driver.getWindowHandles()); + if (tabs.size() > 1) + { + driver.close(); + driver.switchTo().window(tabs.get(tabs.size() - 2)); + } + else + { + driver.quit(); + } // FIXME missing comment on why this is necessary ? (if it really is...) TestUtils.waitMillis(500); diff --git a/src/test/java/org/jitsi/meet/test/base/ParticipantOptions.java b/src/test/java/org/jitsi/meet/test/base/ParticipantOptions.java index b15ac1f29..5556bc9d5 100644 --- a/src/test/java/org/jitsi/meet/test/base/ParticipantOptions.java +++ b/src/test/java/org/jitsi/meet/test/base/ParticipantOptions.java @@ -16,6 +16,7 @@ package org.jitsi.meet.test.base; import org.apache.commons.lang3.*; +import org.openqa.selenium.*; import java.util.*; @@ -56,6 +57,12 @@ public class ParticipantOptions */ private final Properties defaults; + /** + * If we want to re-use an existing driver when creating a participants we can use this field to pass it through. + * The case when creating multiple tabs in a browser window. + */ + private WebDriver driver = null; + /** * Loads values from the configuration into the participant options backend. * @param config - A Properties instance holding configuration @@ -103,6 +110,8 @@ public void putAll(ParticipantOptions toAdd) { setProperty(key, srcBackend.getProperty(key)); } + + setDriver(toAdd.getDriver()); } /** @@ -204,4 +213,14 @@ protected void setProperty(String key, String value) this.backend.remove(key); } } + + public WebDriver getDriver() + { + return driver; + } + + public void setDriver(WebDriver driver) + { + this.driver = driver; + } } diff --git a/src/test/java/org/jitsi/meet/test/web/WebParticipantFactory.java b/src/test/java/org/jitsi/meet/test/web/WebParticipantFactory.java index 747c22e7b..58f5b6b7e 100644 --- a/src/test/java/org/jitsi/meet/test/web/WebParticipantFactory.java +++ b/src/test/java/org/jitsi/meet/test/web/WebParticipantFactory.java @@ -94,10 +94,17 @@ protected WebParticipant doCreateParticipant(ParticipantOptions options) webOptions.putAll(options); + // if we want to re-use a driver/window for multiple participants (create multiple tabs) + WebDriver driver = options.getDriver(); + if (driver == null) + { + driver = startWebDriver(webOptions); + } + WebParticipant webParticipant = new WebParticipant( webOptions.getName(), - startWebDriver(webOptions), + driver, webOptions.getParticipantType(), webOptions.getLoadTest());