openvidu-test-e2e: improve reliability of connectionQualityLostPublisherTest with a total port blackout

pull/910/merge
pabloFuente 2026-09-08 01:02:47 +02:00
parent 7ca83f51b9
commit 77600d6713
2 changed files with 26 additions and 8 deletions

View File

@ -25,6 +25,16 @@ public class NetworkConditioner {
* path is dead). * path is dead).
*/ */
public static final String SFU_ICE_TCP_PORT = "7881"; public static final String SFU_ICE_TCP_PORT = "7881";
/**
* UDP port of the SFU's embedded TURN server (livekit.yaml
* {@code turn.udp_port}). Same reasoning as {@link #SFU_ICE_TCP_PORT}: it is the
* last route out of a media blackout, since a client whose host and srflx
* candidates are all dead falls back to allocating a TURN relay. Blocking the
* listener is enough; the relay range ({@code turn.relay_range_*}) is where the
* TURN server sends from towards the peer, never a destination of the client.
*/
public static final String SFU_TURN_UDP_PORT = "3478";
private static final String DOCKER_SOCK = "/var/run/docker.sock"; private static final String DOCKER_SOCK = "/var/run/docker.sock";
// Where Pumba installs the netem qdisc of a port-scoped netem impairment. Pumba // Where Pumba installs the netem qdisc of a port-scoped netem impairment. Pumba
@ -227,14 +237,16 @@ public class NetworkConditioner {
* Stops the running Pumba first (avoids conflicting root qdiscs), then installs * Stops the running Pumba first (avoids conflicting root qdiscs), then installs
* an iptables OUTPUT DROP rule over the whole UDP {@code mediaPortRange} (e.g. * an iptables OUTPUT DROP rule over the whole UDP {@code mediaPortRange} (e.g.
* "7900-7999") plus one over the SFU's ICE-TCP port * "7900-7999") plus one over the SFU's ICE-TCP port
* ({@link #SFU_ICE_TCP_PORT}), so the browser's reconnect cannot fail over to * ({@link #SFU_ICE_TCP_PORT}) and one over its TURN listener
* TCP and escape the blackout. * ({@link #SFU_TURN_UDP_PORT}), so the browser's reconnect cannot fail over to
* TCP or to a relay candidate and escape the blackout.
*/ */
public static void blackoutOutbound(String targetContainer, String mediaPortRange, int durationSec) { public static void blackoutOutbound(String targetContainer, String mediaPortRange, int durationSec) {
clear(); clear();
final String iptablesRange = mediaPortRange.replace('-', ':'); // iptables ranges are low:high final String iptablesRange = mediaPortRange.replace('-', ':'); // iptables ranges are low:high
log.info("Total OUTBOUND blackout (100% loss) on container {} across SFU media port range {} and " log.info("Total OUTBOUND blackout (100% loss) on container {} across SFU media port range {}, "
+ "ICE-TCP port {} (iptables OUTPUT DROP)", targetContainer, mediaPortRange, SFU_ICE_TCP_PORT); + "ICE-TCP port {} and TURN port {} (iptables OUTPUT DROP)", targetContainer, mediaPortRange,
SFU_ICE_TCP_PORT, SFU_TURN_UDP_PORT);
String out = nettools(targetContainer, "iptables", String out = nettools(targetContainer, "iptables",
"-A OUTPUT -o eth0 -p udp --dport " + iptablesRange + " -j DROP"); "-A OUTPUT -o eth0 -p udp --dport " + iptablesRange + " -j DROP");
log.info("blackout iptables -A OUTPUT (udp {}) result: {}", iptablesRange, out); log.info("blackout iptables -A OUTPUT (udp {}) result: {}", iptablesRange, out);
@ -243,6 +255,9 @@ public class NetworkConditioner {
String outTcp = nettools(targetContainer, "iptables", String outTcp = nettools(targetContainer, "iptables",
"-A OUTPUT -o eth0 -p tcp --dport " + SFU_ICE_TCP_PORT + " -j DROP"); "-A OUTPUT -o eth0 -p tcp --dport " + SFU_ICE_TCP_PORT + " -j DROP");
log.info("blackout iptables -A OUTPUT (tcp {}) result: {}", SFU_ICE_TCP_PORT, outTcp); log.info("blackout iptables -A OUTPUT (tcp {}) result: {}", SFU_ICE_TCP_PORT, outTcp);
String outTurn = nettools(targetContainer, "iptables",
"-A OUTPUT -o eth0 -p udp --dport " + SFU_TURN_UDP_PORT + " -j DROP");
log.info("blackout iptables -A OUTPUT (udp {}) result: {}", SFU_TURN_UDP_PORT, outTurn);
blackoutContainer = targetContainer; blackoutContainer = targetContainer;
} }

View File

@ -721,9 +721,10 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
* then propagate the first failure AssertionError or any exception to the * then propagate the first failure AssertionError or any exception to the
* caller's thread, so assertions inside the tasks actually fail the test (an * caller's thread, so assertions inside the tasks actually fail the test (an
* AssertionError thrown in a worker thread would otherwise be lost). Every * AssertionError thrown in a worker thread would otherwise be lost). Every
* failure is logged and the ones after the first travel as suppressed exceptions * failure is logged and the ones after the first travel as suppressed
* of the propagated one, so no browser's error is masked by another's. Each task * exceptions of the propagated one, so no browser's error is masked by
* MUST drive a distinct WebDriver, since a Selenium driver is not thread-safe. * another's. Each task MUST drive a distinct WebDriver, since a Selenium driver
* is not thread-safe.
*/ */
private void runInParallel(ThrowingRunnable... tasks) throws Exception { private void runInParallel(ThrowingRunnable... tasks) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(tasks.length); ExecutorService executor = Executors.newFixedThreadPool(tasks.length);
@ -1103,10 +1104,12 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
log.info("ConnectionQuality LOST publisher test"); log.info("ConnectionQuality LOST publisher test");
Pair<OpenViduTestappUser, OpenViduTestappUser> users = connectionQualityTest(true, false, 99, null); Pair<OpenViduTestappUser, OpenViduTestappUser> users = connectionQualityTest(true, false, null, null);
OpenViduTestappUser punchbagUser = users.getLeft(); OpenViduTestappUser punchbagUser = users.getLeft();
OpenViduTestappUser regularUser = users.getRight(); OpenViduTestappUser regularUser = users.getRight();
NetworkConditioner.blackoutOutbound(getNetemContainerName(punchbagUser), "7900-7999", 120);
punchbagUser.getEventManager().waitUntilEventReaches(0, "connectionQualityChanged", "RoomEvent", 1); punchbagUser.getEventManager().waitUntilEventReaches(0, "connectionQualityChanged", "RoomEvent", 1);
regularUser.getEventManager().waitUntilEventReaches(0, "connectionQualityChanged", "RoomEvent", 1); regularUser.getEventManager().waitUntilEventReaches(0, "connectionQualityChanged", "RoomEvent", 1);