diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java index 39faca5b..e51646d2 100644 --- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java +++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java @@ -87,9 +87,12 @@ import io.openvidu.java.client.OpenVidu; import io.openvidu.java.client.OpenViduHttpException; import io.openvidu.java.client.OpenViduJavaClientException; import io.openvidu.java.client.OpenViduRole; +import io.openvidu.java.client.Publisher; import io.openvidu.java.client.Recording; import io.openvidu.java.client.Recording.OutputMode; +import io.openvidu.java.client.RecordingLayout; import io.openvidu.java.client.RecordingMode; +import io.openvidu.java.client.RecordingProperties; import io.openvidu.java.client.Session; import io.openvidu.java.client.SessionProperties; import io.openvidu.java.client.TokenOptions; @@ -1291,7 +1294,7 @@ public class OpenViduTestAppE2eTest { String recPath = recordingsPath + sessionName + "/"; Recording recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(sessionName); - this.checkIndividualRecording(recPath, recording, 2, "opus", "vp8"); + this.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true); // Try to get the stopped recording user.getDriver().findElement(By.id("get-recording-btn")).click(); @@ -1516,23 +1519,23 @@ public class OpenViduTestAppE2eTest { String recPath = recordingsPath + SESSION_NAME + "/"; Recording recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME); this.checkMultimediaFile(new File(recPath + recording.getName() + ".mp4"), false, true, recording.getDuration(), - recording.getResolution(), null, "h264"); + recording.getResolution(), null, "h264", true); // Check audio-only COMPOSED recording recPath = recordingsPath + SESSION_NAME + "-1/"; recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME + "-1"); this.checkMultimediaFile(new File(recPath + recording.getName() + ".webm"), true, false, - recording.getDuration(), null, "opus", null); + recording.getDuration(), null, "opus", null, true); // Check video-only INDIVIDUAL recording recPath = recordingsPath + SESSION_NAME + "-2/"; recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME + "-2"); - this.checkIndividualRecording(recPath, recording, 3, "opus", "vp8"); + this.checkIndividualRecording(recPath, recording, 3, "opus", "vp8", true); // Check audio-only INDIVIDUAL recording recPath = recordingsPath + SESSION_NAME + "-3/"; recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME + "-3"); - this.checkIndividualRecording(recPath, recording, 2, "opus", "vp8"); + this.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true); user.getDriver().findElement(By.id("close-dialog-btn")).click(); Thread.sleep(500); @@ -1918,7 +1921,7 @@ public class OpenViduTestAppE2eTest { void openViduJavaClientTest() throws Exception { isRecordingTest = true; - setupBrowser("chrome"); + setupBrowser("chromeAlternateScreenShare"); log.info("openvidu-java-client test"); @@ -1986,6 +1989,9 @@ public class OpenViduTestAppE2eTest { user.getDriver().findElement(By.id("save-btn")).click(); Thread.sleep(1000); + // Moderator sends only video + user.getDriver().findElement(By.cssSelector("#openvidu-instance-0 .active-audio-checkbox")).click(); + user.getDriver().findElements(By.className("join-btn")).forEach(el -> el.sendKeys(Keys.ENTER)); user.getEventManager().waitUntilEventReaches("connectionCreated", 4); @@ -1996,12 +2002,20 @@ public class OpenViduTestAppE2eTest { final int numberOfVideos = user.getDriver().findElements(By.tagName("video")).size(); Assert.assertEquals("Expected 2 videos but found " + numberOfVideos, 2, numberOfVideos); - Assert.assertTrue("Videos were expected to have audio and video tracks", user.getEventManager() - .assertMediaTracks(user.getDriver().findElements(By.tagName("video")), true, true)); + Assert.assertTrue("Moderator video was expected to have audio only track", + user.getEventManager().assertMediaTracks( + (WebElement) user.getDriver().findElement(By.cssSelector("#openvidu-instance-0 video")), false, + true, "#openvidu-instance-0")); + Assert.assertTrue("Subscriber video was expected to have audio and video tracks", + user.getEventManager().assertMediaTracks( + (WebElement) user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 video")), true, + true, "#openvidu-instance-1")); Assert.assertTrue("Session.fetch() should return true after users connected", OV.fetch()); Assert.assertFalse("Session.fetch() should return false after OpenVidu.fetch() has been called", session.fetch()); + + // Verify session properties and status Assert.assertEquals("Wrong sessionId", customSessionId, session.getSessionId()); Assert.assertEquals("Wrong recording mode", RecordingMode.ALWAYS, session.getProperties().recordingMode()); Assert.assertEquals("Wrong default output mode", Recording.OutputMode.INDIVIDUAL, @@ -2021,32 +2035,276 @@ public class OpenViduTestAppE2eTest { } Assert.assertEquals(OpenViduRole.SUBSCRIBER, connectionSubscriber.getRole()); + + // Verify platform Assert.assertTrue("Wrong platform for moderator connection", connectionModerator.getPlatform().startsWith("Chrome")); Assert.assertTrue("Wrong platform for subscriber connection", connectionSubscriber.getPlatform().startsWith("Chrome")); - // connection1 is moderator + // Verify publishers Assert.assertEquals("Expected 1 publisher for connection " + connectionModerator.getConnectionId() + " but found " + connectionModerator.getPublishers().size(), 1, connectionModerator.getPublishers().size()); - Assert.assertEquals("Expected 0 subscribers for connection " + connectionModerator.getConnectionId() - + " but found " + connectionModerator.getSubscribers().size(), 0, - connectionModerator.getSubscribers().size()); Assert.assertEquals("Expected 0 publishers for connection " + connectionSubscriber.getConnectionId() + " but found " + connectionSubscriber.getPublishers().size(), 0, connectionSubscriber.getPublishers().size()); + + // Verify subscribers + Assert.assertEquals("Expected 0 subscribers for connection " + connectionModerator.getConnectionId() + + " but found " + connectionModerator.getSubscribers().size(), 0, + connectionModerator.getSubscribers().size()); Assert.assertEquals( "Expected 1 subscriber for connection " + connectionSubscriber.getConnectionId() + " but found " + connectionSubscriber.getSubscribers().size(), 1, connectionSubscriber.getSubscribers().size()); + Assert.assertEquals("Publisher and subscriber should have same streamId", + connectionModerator.getPublishers().get(0).getStreamId(), connectionSubscriber.getSubscribers().get(0)); + + // Verify server and client data Assert.assertEquals("Server data doesn't match", serverDataModerator, connectionModerator.getServerData()); Assert.assertEquals("Server data doesn't match", serverDataSubscriber, connectionSubscriber.getServerData()); Assert.assertEquals("Client data doesn't match", clientDataModerator, connectionModerator.getClientData()); Assert.assertEquals("Client data doesn't match", clientDataSubscriber, connectionSubscriber.getClientData()); - // Verify that users have the role and data they were assigned through - // TokenOptions + // Verify publisher properties + Publisher pub = connectionModerator.getPublishers().get(0); + Assert.assertEquals("{\"width\":640,\"height\":480}", pub.getVideoDimensions()); + Assert.assertEquals(new Integer(30), pub.getFrameRate()); + Assert.assertEquals("CAMERA", pub.getTypeOfVideo()); + Assert.assertTrue(pub.hasVideo()); + Assert.assertTrue(pub.isVideoActive()); + Assert.assertTrue(pub.hasAudio()); + Assert.assertFalse(pub.isAudioActive()); + + Assert.assertFalse("Session.fetch() should return false", session.fetch()); + Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch()); + + // Change publisher dynamically + user.getDriver().findElement(By.cssSelector("#openvidu-instance-0 .change-publisher-btn")).click(); + + user.getEventManager().waitUntilEventReaches("streamDestroyed", 2); + user.getEventManager().waitUntilEventReaches("accessAllowed", 2); + user.getEventManager().waitUntilEventReaches("streamCreated", 4); + user.getEventManager().waitUntilEventReaches("streamPlaying", 4); + + Assert.assertTrue("Session.fetch() should return true after publisher changed", session.fetch()); + Assert.assertFalse("OpenVidu.fetch() should return false after Session.fetch()", OV.fetch()); + + // Verify new publisher properties + if (OpenViduRole.MODERATOR.equals(session.getActiveConnections().get(0).getRole())) { + connectionModerator = session.getActiveConnections().get(0); + connectionSubscriber = session.getActiveConnections().get(1); + } else { + connectionModerator = session.getActiveConnections().get(1); + connectionSubscriber = session.getActiveConnections().get(0); + } + pub = connectionModerator.getPublishers().get(0); + + String widthAndHeight = user.getEventManager().getDimensionOfViewport(); + JSONObject obj = (JSONObject) new JSONParser().parse(widthAndHeight); + Assert.assertEquals( + "{\"width\":" + ((long) obj.get("width") - 1) + ",\"height\":" + ((long) obj.get("height") - 1) + "}", + pub.getVideoDimensions()); + Assert.assertEquals(new Integer(30), pub.getFrameRate()); + Assert.assertEquals("SCREEN", pub.getTypeOfVideo()); + Assert.assertTrue(pub.hasVideo()); + Assert.assertTrue(pub.isVideoActive()); + Assert.assertFalse(pub.hasAudio()); + Assert.assertNull(pub.isAudioActive()); + + // Test recording + RecordingProperties recordingProperties; + try { + OV.startRecording("NOT_EXISTS"); + } catch (OpenViduHttpException e) { + Assert.assertEquals("Wrong HTTP status on OpenVidu.startRecording()", 404, e.getStatus()); + } + Session sessionAux = OV.createSession(); + Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch()); + try { + OV.startRecording(sessionAux.getSessionId()); + } catch (OpenViduHttpException e) { + Assert.assertEquals("Wrong HTTP status on OpenVidu.startRecording()", 406, e.getStatus()); + } finally { + Assert.assertFalse("Session.fetch() should return false", sessionAux.fetch()); + sessionAux.close(); + try { + sessionAux.fetch(); + } catch (OpenViduHttpException e2) { + Assert.assertEquals("Wrong HTTP status on Session.fetch()", 404, e2.getStatus()); + } + Assert.assertFalse("OpenVidu.fetch() should return true", OV.fetch()); + } + try { + recordingProperties = new RecordingProperties.Builder().hasAudio(false).hasVideo(false).build(); + OV.startRecording(session.getSessionId(), recordingProperties); + } catch (OpenViduHttpException e) { + Assert.assertEquals("Wrong HTTP status on OpenVidu.startRecording()", 422, e.getStatus()); + } + try { + recordingProperties = new RecordingProperties.Builder().resolution("99x1080").build(); + OV.startRecording(session.getSessionId(), recordingProperties); + } catch (OpenViduHttpException e) { + Assert.assertEquals("Wrong HTTP status on OpenVidu.startRecording()", 422, e.getStatus()); + } + try { + OV.startRecording(session.getSessionId()); + } catch (OpenViduHttpException e) { + Assert.assertEquals("Wrong HTTP status on OpenVidu.startRecording()", 409, e.getStatus()); + } + + List recordings = OV.listRecordings(); + Assert.assertEquals("There should be only 1 recording", 1, recordings.size()); + Recording recording = recordings.get(0); + Assert.assertEquals("Recording id and name should be equal", recording.getId(), recording.getName()); + Assert.assertEquals("Recording id and sessionId should be equal", session.getSessionId(), recording.getId()); + + // Check ongoing recording properties + Assert.assertEquals("Wrong recording session id", session.getSessionId(), recording.getSessionId()); + Assert.assertEquals("Wrong recording duration", 0, recording.getDuration(), 0.0001); + Assert.assertEquals("Wrong recording size", 0, recording.getSize()); + Assert.assertNull("Wrong recording url", recording.getUrl()); + Assert.assertEquals("Wrong recording output mode", Recording.OutputMode.INDIVIDUAL, recording.getOutputMode()); + Assert.assertNull("Wrong recording layout", recording.getRecordingLayout()); + Assert.assertNull("Wrong recording custom layout", recording.getCustomLayout()); + Assert.assertNull("Wrong recording resolution", recording.getResolution()); + Assert.assertEquals("Wrong recording status", Recording.Status.started, recording.getStatus()); + Assert.assertTrue("Wrong recording hasAudio", recording.hasAudio()); + Assert.assertTrue("Wrong recording hasVideo", recording.hasVideo()); + + try { + OV.stopRecording("NOT_EXISTS"); + } catch (OpenViduHttpException e) { + Assert.assertEquals("Wrong HTTP status on OpenVidu.startRecording()", 404, e.getStatus()); + } + recording = OV.stopRecording(recording.getId()); + + user.getEventManager().waitUntilEventReaches("recordingStopped", 1); + + Assert.assertTrue("Wrong recording duration", recording.getDuration() > 0); + Assert.assertTrue("Wrong recording size", recording.getSize() > 0); + Assert.assertNull("Wrong recording url", recording.getUrl()); + Assert.assertEquals("Wrong recording status", Recording.Status.stopped, recording.getStatus()); + Assert.assertFalse("Session shouldn't be being recorded", session.isBeingRecorded()); + Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch()); + + this.checkIndividualRecording("/opt/openvidu/recordings/" + customSessionId + "/", recording, 2, "opus", "vp8", + false); + + user.getDriver().findElement(By.cssSelector("#openvidu-instance-0 .change-publisher-btn")).click(); + user.getEventManager().waitUntilEventReaches("streamDestroyed", 4); + user.getEventManager().waitUntilEventReaches("accessAllowed", 3); + user.getEventManager().waitUntilEventReaches("streamCreated", 6); + user.getEventManager().waitUntilEventReaches("streamPlaying", 6); + + Assert.assertTrue("Session.fetch() should return true", session.fetch()); + + final String customRecordingName = "CUSTOM_NAME"; + recordingProperties = new RecordingProperties.Builder().outputMode(Recording.OutputMode.COMPOSED) + .recordingLayout(RecordingLayout.BEST_FIT).resolution("1280x720").hasVideo(true).hasAudio(false) + .name(customRecordingName).build(); + + Recording recording2 = OV.startRecording(session.getSessionId(), recordingProperties); + recording2 = OV.stopRecording(recording2.getId()); + Assert.assertEquals("Wrong recording status", Recording.Status.failed, recording2.getStatus()); + OV.deleteRecording(recording2.getId()); + + recording2 = OV.startRecording(session.getSessionId(), recordingProperties); + user.getEventManager().waitUntilEventReaches("recordingStarted", 3); + + Assert.assertEquals("Wrong recording name", customRecordingName, recording2.getName()); + Assert.assertEquals("Wrong recording id", session.getSessionId() + "-1", recording2.getId()); + Assert.assertEquals("Wrong recording session id", session.getSessionId(), recording2.getSessionId()); + Assert.assertEquals("Wrong recording duration", 0, recording2.getDuration(), 0.0001); + Assert.assertEquals("Wrong recording size", 0, recording2.getSize()); + Assert.assertNull("Wrong recording url", recording2.getUrl()); + Assert.assertEquals("Wrong recording output mode", Recording.OutputMode.COMPOSED, recording2.getOutputMode()); + Assert.assertEquals("Wrong recording layout", RecordingLayout.BEST_FIT, recording2.getRecordingLayout()); + Assert.assertNull("Wrong recording custom layout", recording2.getCustomLayout()); + Assert.assertEquals("Wrong recording resolution", "1280x720", recording2.getResolution()); + Assert.assertEquals("Wrong recording status", Recording.Status.started, recording2.getStatus()); + Assert.assertFalse("Wrong recording hasAudio", recording2.hasAudio()); + Assert.assertTrue("Wrong recording hasVideo", recording2.hasVideo()); + + Thread.sleep(5000); + + recording2 = OV.stopRecording(recording2.getId()); + + user.getEventManager().waitUntilEventReaches("recordingStopped", 3); + + Assert.assertTrue("Wrong recording duration", recording2.getDuration() > 0); + Assert.assertTrue("Wrong recording size", recording2.getSize() > 0); + Assert.assertNull("Wrong recording url", recording2.getUrl()); + Assert.assertEquals("Wrong recording status", Recording.Status.stopped, recording2.getStatus()); + Assert.assertFalse("Session shouldn't be being recorded", session.isBeingRecorded()); + Assert.assertFalse("Session.fetch() should return false", session.fetch()); + + String recordingsPath = "/opt/openvidu/recordings/" + customSessionId + "-1/"; + File file1 = new File(recordingsPath + customRecordingName + ".mp4"); + File file2 = new File(recordingsPath + ".recording." + recording2.getId()); + File file3 = new File(recordingsPath + recording2.getId() + ".jpg"); + + Assert.assertTrue("File " + file1.getAbsolutePath() + " does not exist or is empty", + file1.exists() && file1.length() > 0); + Assert.assertTrue("File " + file2.getAbsolutePath() + " does not exist or is empty", + file2.exists() && file2.length() > 0); + Assert.assertTrue("File " + file3.getAbsolutePath() + " does not exist or is empty", + file3.exists() && file3.length() > 0); + + Assert.assertTrue("Recorded file " + file1.getAbsolutePath() + " is not fine", + this.recordedFileFine(file1, recording2)); + Assert.assertTrue("Thumbnail " + file3.getAbsolutePath() + " is not fine", this.thumbnailIsFine(file3)); + + try { + OV.deleteRecording("NOT_EXISTS"); + } catch (OpenViduHttpException e) { + Assert.assertEquals("Wrong HTTP status on OpenVidu.startRecording()", 404, e.getStatus()); + } + OV.deleteRecording(recording.getId()); + OV.deleteRecording(recording2.getId()); + + Assert.assertEquals("There shouldn't be any recordings", 0, OV.listRecordings().size()); + + try { + session.forceUnpublish("NOT_EXISTS"); + } catch (OpenViduHttpException e) { + Assert.assertEquals("Wrong HTTP status on Session.fetch()", 404, e.getStatus()); + } + try { + session.forceDisconnect("NOT_EXISTS"); + } catch (OpenViduHttpException e) { + Assert.assertEquals("Wrong HTTP status on Session.fetch()", 404, e.getStatus()); + } + + if (OpenViduRole.MODERATOR.equals(session.getActiveConnections().get(0).getRole())) { + connectionModerator = session.getActiveConnections().get(0); + connectionSubscriber = session.getActiveConnections().get(1); + } else { + connectionModerator = session.getActiveConnections().get(1); + connectionSubscriber = session.getActiveConnections().get(0); + } + pub = connectionModerator.getPublishers().get(0); + + session.forceUnpublish(pub); + user.getEventManager().waitUntilEventReaches("streamDestroyed", 6); + Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch()); + + session.getActiveConnections().forEach(con -> { + Assert.assertEquals("Wrong number of Publishers", 0, con.getPublishers().size()); + Assert.assertEquals("Wrong number of Subscribers", 0, con.getSubscribers().size()); + }); + + session.forceDisconnect(connectionModerator); + user.getEventManager().waitUntilEventReaches("sessionDisconnected", 1); + user.getEventManager().waitUntilEventReaches("connectionDestroyed", 1); + Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch()); + + session.close(); + + user.getEventManager().waitUntilEventReaches("sessionDisconnected", 2); + + Assert.assertFalse("Session.fetch() should return true", OV.fetch()); gracefullyLeaveParticipants(2); } @@ -2343,7 +2601,8 @@ public class OpenViduTestAppE2eTest { } private boolean recordedFileFine(File file, Recording recording) { - this.checkMultimediaFile(file, true, true, recording.getDuration(), recording.getResolution(), "aac", "h264"); + this.checkMultimediaFile(file, recording.hasAudio(), recording.hasVideo(), recording.getDuration(), + recording.getResolution(), "aac", "h264", true); boolean isFine = false; Picture frame; @@ -2369,7 +2628,7 @@ public class OpenViduTestAppE2eTest { } private void checkIndividualRecording(String recPath, Recording recording, int numberOfVideoFiles, - String audioDecoder, String videoDecoder) { + String audioDecoder, String videoDecoder, boolean checkAudio) { // Should be only 2 files: zip and metadata File folder = new File(recPath); @@ -2432,7 +2691,7 @@ public class OpenViduTestAppE2eTest { log.info("Duration of {} according to sync metadata json file: {} s", webmFile.getName(), durationInSeconds); this.checkMultimediaFile(webmFile, recording.hasAudio(), recording.hasVideo(), durationInSeconds, - recording.getResolution(), audioDecoder, videoDecoder); + recording.getResolution(), audioDecoder, videoDecoder, checkAudio); webmFile.delete(); } @@ -2443,23 +2702,27 @@ public class OpenViduTestAppE2eTest { } private void checkMultimediaFile(File file, boolean hasAudio, boolean hasVideo, double duration, String resolution, - String audioDecoder, String videoDecoder) { + String audioDecoder, String videoDecoder, boolean checkAudio) { // Check tracks, duration, resolution, framerate and decoders MultimediaFileMetadata metadata = new MultimediaFileMetadata(file.getAbsolutePath()); if (hasVideo) { - if (hasAudio) { - Assert.assertTrue(metadata.hasAudio() && metadata.hasVideo()); - Assert.assertTrue(metadata.getAudioDecoder().toLowerCase().contains(audioDecoder)); - } else { - Assert.assertTrue(metadata.hasVideo()); - Assert.assertFalse(metadata.hasAudio()); + if (checkAudio) { + if (hasAudio) { + Assert.assertTrue("Media file " + file.getAbsolutePath() + " should have audio", + metadata.hasAudio() && metadata.hasVideo()); + Assert.assertTrue(metadata.getAudioDecoder().toLowerCase().contains(audioDecoder)); + } else { + Assert.assertTrue("Media file " + file.getAbsolutePath() + " should have video", + metadata.hasVideo()); + Assert.assertFalse(metadata.hasAudio()); + } } if (resolution != null) { Assert.assertEquals(resolution, metadata.getVideoWidth() + "x" + metadata.getVideoHeight()); } Assert.assertTrue(metadata.getVideoDecoder().toLowerCase().contains(videoDecoder)); - } else if (hasAudio) { + } else if (hasAudio && checkAudio) { Assert.assertTrue(metadata.hasAudio()); Assert.assertFalse(metadata.hasVideo()); Assert.assertTrue(metadata.getAudioDecoder().toLowerCase().contains(audioDecoder));