From 7f99c48e2ca73f3684bd9ca29f84d9433f55d790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillaume=20T=C3=A2che?= Date: Sat, 21 Sep 2024 21:22:30 +0200 Subject: [PATCH] Adds tests for FFmpeg --- .../ffmpeg/FFmpegVideoConverter.java | 17 +- .../ffmpeg/FFprobeVideoLoader.java | 22 +- .../ffmpeg/TestFFmpegVideoConverter.java | 311 ++++++++++++++++-- .../ffmpeg/TestFFmpegVideoLoader.java | 80 +++-- .../autosubtitle/ffmpeg/fake-ffmpeg.exe | Bin 25600 -> 0 bytes .../autosubtitle/ffmpeg/fake-ffmpeg.ps1 | 5 - .../gtache/autosubtitle/ffmpeg/fake-ffmpeg.sh | 6 - .../autosubtitle/ffmpeg/fake-ffprobe.exe | Bin 25600 -> 0 bytes .../autosubtitle/ffmpeg/fake-ffprobe.ps1 | 6 - .../autosubtitle/ffmpeg/fake-ffprobe.sh | 7 - pom.xml | 25 ++ .../extractor/whisper/AudioOrVideo.java | 4 + .../extractor/whisper/TestAudioOrVideo.java | 67 ++++ 13 files changed, 448 insertions(+), 102 deletions(-) delete mode 100644 ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffmpeg.exe delete mode 100644 ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffmpeg.ps1 delete mode 100644 ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffmpeg.sh delete mode 100644 ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffprobe.exe delete mode 100644 ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffprobe.ps1 delete mode 100644 ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffprobe.sh create mode 100644 whisper/common/src/test/java/com/github/gtache/autosubtitle/subtitle/extractor/whisper/TestAudioOrVideo.java diff --git a/ffmpeg/src/main/java/com/github/gtache/autosubtitle/ffmpeg/FFmpegVideoConverter.java b/ffmpeg/src/main/java/com/github/gtache/autosubtitle/ffmpeg/FFmpegVideoConverter.java index 0953eab..3550529 100644 --- a/ffmpeg/src/main/java/com/github/gtache/autosubtitle/ffmpeg/FFmpegVideoConverter.java +++ b/ffmpeg/src/main/java/com/github/gtache/autosubtitle/ffmpeg/FFmpegVideoConverter.java @@ -8,7 +8,6 @@ import com.github.gtache.autosubtitle.VideoInfo; import com.github.gtache.autosubtitle.impl.AudioInfoImpl; import com.github.gtache.autosubtitle.impl.FileAudioImpl; import com.github.gtache.autosubtitle.impl.FileVideoImpl; -import com.github.gtache.autosubtitle.impl.VideoInfoImpl; import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFmpegBundledPath; import com.github.gtache.autosubtitle.modules.setup.ffmpeg.FFmpegSystemPath; import com.github.gtache.autosubtitle.process.ProcessRunner; @@ -60,7 +59,7 @@ public class FFmpegVideoConverter implements VideoConverter { public Video addSoftSubtitles(final Video video, final Collection> subtitles) throws IOException { final var out = getTempFile(video.info().format()); addSoftSubtitles(video, subtitles, out); - return new FileVideoImpl(out, new VideoInfoImpl(video.info().format(), video.info().width(), video.info().height(), video.info().duration())); + return new FileVideoImpl(out, video.info()); } @Override @@ -105,7 +104,7 @@ public class FFmpegVideoConverter implements VideoConverter { args.add("language=" + c.language().iso3()); }); args.add(path.toString()); - runListen(args, Duration.ofHours(1)); + runLog(args, Duration.ofHours(1)); } @Override @@ -123,13 +122,14 @@ public class FFmpegVideoConverter implements VideoConverter { final var subtitleArg = getSubtitleConverter().formatName().equalsIgnoreCase("ass") ? "ass='" + escapedPath + "'" : "subtitles='" + escapedPath + "'"; final var args = List.of( getFFmpegPath(), + "-y", "-i", videoPath.toString(), "-vf", subtitleArg, path.toString() ); - runListen(args, Duration.ofHours(1)); + runLog(args, Duration.ofHours(1)); } private SubtitleConverter getSubtitleConverter() { @@ -158,7 +158,7 @@ public class FFmpegVideoConverter implements VideoConverter { "0:v", dumpVideoPath.toString() ); - runListen(args, Duration.ofHours(1)); + runLog(args, Duration.ofHours(1)); Files.deleteIfExists(dumpVideoPath); return new FileAudioImpl(audioPath, new AudioInfoImpl("wav", video.info().duration())); } @@ -203,7 +203,7 @@ public class FFmpegVideoConverter implements VideoConverter { return Files.isRegularFile(bundledPath) ? bundledPath.toString() : systemPath.toString(); } - private void runListen(final List args, final Duration duration) throws IOException { + private void runLog(final List args, final Duration duration) throws IOException { final var listener = processRunner.startListen(args); var line = listener.readLine(); final var processName = args.getFirst(); @@ -211,6 +211,9 @@ public class FFmpegVideoConverter implements VideoConverter { logger.info("[{}]: {}", processName, line); line = listener.readLine(); } - listener.join(duration); + final var result = listener.join(duration); + if (result.exitCode() != 0) { + throw new IOException("FFmpeg error : " + result.exitCode() + " - " + result.output()); + } } } diff --git a/ffmpeg/src/main/java/com/github/gtache/autosubtitle/ffmpeg/FFprobeVideoLoader.java b/ffmpeg/src/main/java/com/github/gtache/autosubtitle/ffmpeg/FFprobeVideoLoader.java index 0b0bb40..05a029d 100644 --- a/ffmpeg/src/main/java/com/github/gtache/autosubtitle/ffmpeg/FFprobeVideoLoader.java +++ b/ffmpeg/src/main/java/com/github/gtache/autosubtitle/ffmpeg/FFprobeVideoLoader.java @@ -37,15 +37,19 @@ public class FFprobeVideoLoader implements VideoLoader { @Override public Video loadVideo(final Path path) throws IOException { final var result = processRunner.run(List.of(getFFprobePath(), "-v", "error", "-select_streams", "v", "-show_entries", "stream=width,height,duration", "-of", "csv=p=0", path.toString()), Duration.ofSeconds(5)); - final var resolution = result.output().getLast(); - final var split = resolution.split(","); - final var width = Integer.parseInt(split[0]); - final var height = Integer.parseInt(split[1]); - final var filename = path.getFileName().toString(); - final var extension = filename.substring(filename.lastIndexOf('.') + 1); - final var duration = (long) (Double.parseDouble(split[2]) * 1000L); - final var info = new VideoInfoImpl(extension, width, height, duration); - return new FileVideoImpl(path, info); + if (result.exitCode() == 0) { + final var resolution = result.output().getLast(); + final var split = resolution.split(","); + final var width = Integer.parseInt(split[0]); + final var height = Integer.parseInt(split[1]); + final var filename = path.getFileName().toString(); + final var extension = filename.substring(filename.lastIndexOf('.') + 1); + final var duration = (long) (Double.parseDouble(split[2]) * 1000L); + final var info = new VideoInfoImpl(extension, width, height, duration); + return new FileVideoImpl(path, info); + } else { + throw new IOException("FFprobe error : " + result.exitCode() + " - " + result.output()); + } } private String getFFprobePath() { diff --git a/ffmpeg/src/test/java/com/github/gtache/autosubtitle/ffmpeg/TestFFmpegVideoConverter.java b/ffmpeg/src/test/java/com/github/gtache/autosubtitle/ffmpeg/TestFFmpegVideoConverter.java index 62d4074..d84ceb3 100644 --- a/ffmpeg/src/test/java/com/github/gtache/autosubtitle/ffmpeg/TestFFmpegVideoConverter.java +++ b/ffmpeg/src/test/java/com/github/gtache/autosubtitle/ffmpeg/TestFFmpegVideoConverter.java @@ -1,66 +1,311 @@ package com.github.gtache.autosubtitle.ffmpeg; +import com.github.gtache.autosubtitle.Language; import com.github.gtache.autosubtitle.Video; import com.github.gtache.autosubtitle.VideoInfo; -import com.github.gtache.autosubtitle.impl.OS; +import com.github.gtache.autosubtitle.impl.AudioInfoImpl; +import com.github.gtache.autosubtitle.impl.FileVideoImpl; +import com.github.gtache.autosubtitle.process.ProcessListener; +import com.github.gtache.autosubtitle.process.ProcessResult; import com.github.gtache.autosubtitle.process.ProcessRunner; -import com.github.gtache.autosubtitle.subtitle.Subtitle; import com.github.gtache.autosubtitle.subtitle.SubtitleCollection; import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter; import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider; -import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.Objects; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.prefs.Preferences; -import static org.mockito.Mockito.when; +import static java.util.Objects.requireNonNull; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class TestFFmpegVideoConverter { - private final FFmpegVideoConverter converter; private final ProcessRunner runner; - private final SubtitleConverter subtitleConverter; + private final ProcessListener listener; + private final ProcessResult result; + private final SubtitleConverter subtitleConverter; private final SubtitleConverterProvider subtitleConverterProvider; private final Video video; + private final InputStream in; private final VideoInfo videoInfo; - private final Path tmpFile; - private final Path outputPath; - private final SubtitleCollection collection; + private final SubtitleCollection frCollection; + private final SubtitleCollection deCollection; private final Preferences preferences; + private final Path systemPath; - TestFFmpegVideoConverter(@Mock final SubtitleConverter subtitleConverter, @Mock final ProcessRunner runner, @Mock final SubtitleConverterProvider subtitleConverterProvider, @Mock final Video video, - @Mock final VideoInfo videoInfo, @Mock final SubtitleCollection collection, @Mock final Preferences preferences) throws IOException { - final var output = (OS.getOS() == OS.WINDOWS ? System.getProperty("java.io.tmpdir") : "/tmp"); - final var resource = OS.getOS() == OS.WINDOWS ? "fake-ffmpeg.exe" : "fake-ffmpeg.sh"; - this.video = Objects.requireNonNull(video); - this.runner = Objects.requireNonNull(runner); - this.videoInfo = Objects.requireNonNull(videoInfo); + TestFFmpegVideoConverter(@Mock final SubtitleConverter subtitleConverter, @Mock final ProcessRunner runner, + @Mock final SubtitleConverterProvider subtitleConverterProvider, @Mock final Video video, + @Mock final VideoInfo videoInfo, @Mock final SubtitleCollection frCollection, @Mock final SubtitleCollection deCollection, + @Mock final Preferences preferences, @Mock final ProcessListener listener, @Mock final InputStream in, + @Mock final ProcessResult result) { + this.video = requireNonNull(video); + this.runner = requireNonNull(runner); + this.result = requireNonNull(result); + this.videoInfo = requireNonNull(videoInfo); + this.subtitleConverter = requireNonNull(subtitleConverter); + this.subtitleConverterProvider = requireNonNull(subtitleConverterProvider); + this.preferences = requireNonNull(preferences); + this.listener = requireNonNull(listener); + this.in = requireNonNull(in); + this.systemPath = Paths.get("system"); + this.frCollection = requireNonNull(frCollection); + this.deCollection = requireNonNull(deCollection); + } + + @BeforeEach + void beforeEach() throws IOException { when(video.info()).thenReturn(videoInfo); - this.tmpFile = Files.createTempFile("fake-ffmpeg", resource.substring(resource.lastIndexOf('.'))); - try (final var in = getClass().getResourceAsStream(resource)) { - Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING); - } - this.outputPath = Path.of(output, "test-ffmpeg-output.txt"); - this.subtitleConverter = Objects.requireNonNull(subtitleConverter); - this.subtitleConverterProvider = Objects.requireNonNull(subtitleConverterProvider); - this.preferences = Objects.requireNonNull(preferences); - this.converter = new FFmpegVideoConverter(tmpFile, tmpFile, subtitleConverterProvider, preferences, runner); - this.collection = Objects.requireNonNull(collection); + when(subtitleConverterProvider.getConverter("srt")).thenReturn((SubtitleConverter) subtitleConverter); + when(subtitleConverter.formatName()).thenReturn("srt"); + when(subtitleConverter.format(any(), any())).thenReturn(""); + when(preferences.get("outputFormat", "srt")).thenReturn("srt"); + when(video.getInputStream()).thenReturn(in); + when(frCollection.language()).thenReturn(Language.FR); + when(deCollection.language()).thenReturn(Language.DE); + when(runner.startListen(anyList())).thenReturn(listener); + when(listener.join(Duration.ofHours(1))).thenReturn(result); + when(result.exitCode()).thenReturn(0); } - @AfterEach - void afterEach() throws IOException { - Files.deleteIfExists(tmpFile); - Files.deleteIfExists(outputPath); + @Test + void testAddSoftSubtitlesMp4() throws IOException { + when(videoInfo.format()).thenReturn("mp4"); + final var converter = new FFmpegVideoConverter(Paths.get("bundled"), systemPath, subtitleConverterProvider, preferences, runner); + final var result = converter.addSoftSubtitles(video, List.of(frCollection, deCollection)); + + assertEquals(videoInfo, result.info()); + + final var args = getArgs(); + assertEquals(27, args.size()); + final var argMap = new HashMap(); + argMap.put(0, systemPath.toString()); + argMap.put(1, "-y"); + argMap.put(2, "-i"); + argMap.put(4, "-i"); + argMap.put(6, "-i"); + argMap.put(8, "-map"); + argMap.put(9, "0:v"); + argMap.put(10, "-map"); + argMap.put(11, "0:a"); + argMap.put(12, "-map"); + argMap.put(13, "1"); + argMap.put(14, "-map"); + argMap.put(15, "2"); + argMap.put(16, "-c:v"); + argMap.put(17, "copy"); + argMap.put(18, "-c:a"); + argMap.put(19, "copy"); + argMap.put(20, "-c:s"); + argMap.put(21, "mov_text"); + argMap.put(22, "-metadata:s:s:0"); + argMap.put(23, "language=fra"); + argMap.put(24, "-metadata:s:s:1"); + argMap.put(25, "language=deu"); + + checkEquals(argMap, args); + assertTrue(args.get(3).endsWith(".mp4")); + assertTrue(args.get(5).endsWith(".srt")); + assertTrue(args.get(7).endsWith(".srt")); + assertTrue(args.get(26).endsWith(".mp4")); + verify(subtitleConverter).format(frCollection, videoInfo); + verify(subtitleConverter).format(deCollection, videoInfo); + verify(in).transferTo(any()); } - //TODO tests + private static void checkEquals(final Map argMap, final List args) { + argMap.forEach((key, value) -> assertEquals(value, args.get(key))); + } + + @Test + void testAddSoftSubtitlesMkvPathFile() throws IOException { + when(videoInfo.format()).thenReturn("mkv"); + doReturn(null).when(subtitleConverterProvider).getConverter("srt"); + when(subtitleConverterProvider.getConverter("ass")).thenReturn((SubtitleConverter) subtitleConverter); + when(subtitleConverter.formatName()).thenReturn("ass"); + when(preferences.get("outputFormat", "srt")).thenReturn("ass"); + final var fileVideo = mock(FileVideoImpl.class); + when(fileVideo.info()).thenReturn(videoInfo); + final var path = Paths.get("path.mkv"); + when(fileVideo.path()).thenReturn(path); + final var out = Paths.get("out.mkv"); + final var converter = new FFmpegVideoConverter(Paths.get("bundled"), systemPath, subtitleConverterProvider, preferences, runner); + converter.addSoftSubtitles(fileVideo, List.of(frCollection, deCollection), out); + + final var args = getArgs(); + assertEquals(27, args.size()); + final var argMap = new HashMap(); + argMap.put(0, systemPath.toString()); + argMap.put(1, "-y"); + argMap.put(2, "-i"); + argMap.put(3, path.toString()); + argMap.put(4, "-i"); + argMap.put(6, "-i"); + argMap.put(8, "-map"); + argMap.put(9, "0:v"); + argMap.put(10, "-map"); + argMap.put(11, "0:a"); + argMap.put(12, "-map"); + argMap.put(13, "1"); + argMap.put(14, "-map"); + argMap.put(15, "2"); + argMap.put(16, "-c:v"); + argMap.put(17, "copy"); + argMap.put(18, "-c:a"); + argMap.put(19, "copy"); + argMap.put(20, "-c:s"); + argMap.put(21, "copy"); + argMap.put(22, "-metadata:s:s:0"); + argMap.put(23, "language=fra"); + argMap.put(24, "-metadata:s:s:1"); + argMap.put(25, "language=deu"); + argMap.put(26, out.toString()); + + checkEquals(argMap, args); + assertTrue(args.get(5).endsWith(".ass")); + assertTrue(args.get(7).endsWith(".ass")); + verify(subtitleConverter).format(frCollection, videoInfo); + verify(subtitleConverter).format(deCollection, videoInfo); + verifyNoInteractions(in); + } + + @Test + void testAddHardSubtitles() throws IOException { + when(videoInfo.format()).thenReturn("mp4"); + final var converter = new FFmpegVideoConverter(Paths.get("bundled"), systemPath, subtitleConverterProvider, preferences, runner); + final var result = converter.addHardSubtitles(video, frCollection); + + assertEquals(videoInfo, result.info()); + + final var args = getArgs(); + assertEquals(7, args.size()); + final var argMap = new HashMap(); + argMap.put(0, systemPath.toString()); + argMap.put(1, "-y"); + argMap.put(2, "-i"); + argMap.put(4, "-vf"); + + checkEquals(argMap, args); + assertTrue(args.get(3).endsWith(".mp4")); + assertTrue(args.get(5).startsWith("subtitles=")); + assertTrue(args.get(6).endsWith(".mp4")); + verify(subtitleConverter).format(frCollection, videoInfo); + verify(in).transferTo(any()); + } + + @Test + void testAddHardAssSubtitles() throws IOException { + doReturn(null).when(subtitleConverterProvider).getConverter("srt"); + when(subtitleConverterProvider.getConverter("ass")).thenReturn((SubtitleConverter) subtitleConverter); + when(subtitleConverter.formatName()).thenReturn("ass"); + when(preferences.get("outputFormat", "srt")).thenReturn("ass"); + when(videoInfo.format()).thenReturn("mp4"); + final var converter = new FFmpegVideoConverter(Paths.get("bundled"), systemPath, subtitleConverterProvider, preferences, runner); + final var result = converter.addHardSubtitles(video, frCollection); + + assertEquals(videoInfo, result.info()); + + final var args = getArgs(); + assertEquals(7, args.size()); + final var argMap = new HashMap(); + argMap.put(0, systemPath.toString()); + argMap.put(1, "-y"); + argMap.put(2, "-i"); + argMap.put(4, "-vf"); + + checkEquals(argMap, args); + assertTrue(args.get(3).endsWith(".mp4")); + assertTrue(args.get(5).startsWith("ass=")); + assertTrue(args.get(6).endsWith(".mp4")); + verify(subtitleConverter).format(frCollection, videoInfo); + verify(in).transferTo(any()); + } + + @Test + void testGetAudio() throws IOException { + when(videoInfo.format()).thenReturn("mp4"); + when(videoInfo.duration()).thenReturn(25000L); + final var converter = new FFmpegVideoConverter(Paths.get("bundled"), systemPath, subtitleConverterProvider, preferences, runner); + final var result = converter.getAudio(video); + + assertEquals(new AudioInfoImpl("wav", 25000L), result.info()); + + final var args = getArgs(); + assertEquals(10, args.size()); + final var argMap = new HashMap(); + argMap.put(0, systemPath.toString()); + argMap.put(1, "-y"); + argMap.put(2, "-i"); + argMap.put(4, "-map"); + argMap.put(5, "0:a"); + argMap.put(7, "-map"); + argMap.put(8, "0:v"); + + checkEquals(argMap, args); + assertTrue(args.get(3).endsWith(".mp4")); + assertTrue(args.get(6).endsWith(".wav")); + assertTrue(args.get(9).endsWith(".mp4")); + verify(in).transferTo(any()); + } + + @Test + void testGetAudioException() { + when(result.exitCode()).thenReturn(1); + when(videoInfo.format()).thenReturn("mp4"); + when(videoInfo.duration()).thenReturn(25000L); + final var converter = new FFmpegVideoConverter(Paths.get("bundled"), systemPath, subtitleConverterProvider, preferences, runner); + assertThrows(IOException.class, () -> converter.getAudio(video)); + } + + @Test + void testGetAudioBundled(@TempDir final Path tempDir) throws IOException { + final var bundled = tempDir.resolve("ffmpeg"); + Files.createFile(bundled); + when(videoInfo.format()).thenReturn("mp4"); + when(videoInfo.duration()).thenReturn(25000L); + final var converter = new FFmpegVideoConverter(bundled, systemPath, subtitleConverterProvider, preferences, runner); + final var result = converter.getAudio(video); + + assertEquals(new AudioInfoImpl("wav", 25000L), result.info()); + + final var args = getArgs(); + assertEquals(10, args.size()); + final var argMap = new HashMap(); + argMap.put(0, bundled.toString()); + argMap.put(1, "-y"); + argMap.put(2, "-i"); + argMap.put(4, "-map"); + argMap.put(5, "0:a"); + argMap.put(7, "-map"); + argMap.put(8, "0:v"); + + checkEquals(argMap, args); + assertTrue(args.get(3).endsWith(".mp4")); + assertTrue(args.get(6).endsWith(".wav")); + assertTrue(args.get(9).endsWith(".mp4")); + verify(in).transferTo(any()); + } + + private List getArgs() throws IOException { + final var captor = ArgumentCaptor.forClass(List.class); + verify(runner).startListen(captor.capture()); + return captor.getValue(); + } } diff --git a/ffmpeg/src/test/java/com/github/gtache/autosubtitle/ffmpeg/TestFFmpegVideoLoader.java b/ffmpeg/src/test/java/com/github/gtache/autosubtitle/ffmpeg/TestFFmpegVideoLoader.java index 9143f38..a6111ec 100644 --- a/ffmpeg/src/test/java/com/github/gtache/autosubtitle/ffmpeg/TestFFmpegVideoLoader.java +++ b/ffmpeg/src/test/java/com/github/gtache/autosubtitle/ffmpeg/TestFFmpegVideoLoader.java @@ -1,13 +1,13 @@ package com.github.gtache.autosubtitle.ffmpeg; import com.github.gtache.autosubtitle.impl.FileVideoImpl; -import com.github.gtache.autosubtitle.impl.OS; import com.github.gtache.autosubtitle.impl.VideoInfoImpl; +import com.github.gtache.autosubtitle.process.ProcessResult; import com.github.gtache.autosubtitle.process.ProcessRunner; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -15,48 +15,70 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; +import java.time.Duration; +import java.util.List; import java.util.Objects; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class TestFFmpegVideoLoader { - private final FFprobeVideoLoader loader; private final ProcessRunner runner; - private final Path tmpFile; - private final Path outputPath; + private final ProcessResult result; + private final Path systemPath; + private final Path in; - TestFFmpegVideoLoader(@Mock final ProcessRunner runner) throws IOException { - final var output = (OS.getOS() == OS.WINDOWS ? System.getProperty("java.io.tmpdir") : "/tmp"); - final var resource = OS.getOS() == OS.WINDOWS ? "fake-ffprobe.exe" : "fake-ffprobe.sh"; - this.tmpFile = Files.createTempFile("fake-ffprobe", resource.substring(resource.lastIndexOf('.'))); - try (final var in = getClass().getResourceAsStream(resource)) { - if (in == null) { - throw new IOException("Unable to find " + resource); - } - Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING); - } - this.outputPath = Path.of(output, "test-ffprobe-output.txt"); + TestFFmpegVideoLoader(@Mock final ProcessRunner runner, @Mock final ProcessResult result) { this.runner = Objects.requireNonNull(runner); - this.loader = new FFprobeVideoLoader(tmpFile, tmpFile, runner); + this.result = Objects.requireNonNull(result); + this.systemPath = Paths.get("system"); + this.in = Paths.get("in.mp4"); } - @AfterEach - void afterEach() throws IOException { - Files.deleteIfExists(tmpFile); - Files.deleteIfExists(outputPath); + @BeforeEach + void beforeEach() { + when(result.exitCode()).thenReturn(0); + when(result.output()).thenReturn(List.of("1920,1080,25")); } @Test - @Disabled("Doesn't work") - void testLoadVideo() throws IOException { - final var in = Paths.get("in.mp4"); + void testLoadVideoBundled(@TempDir final Path tempDir) throws IOException { + final var bundledPath = tempDir.resolve("ffprobe"); + Files.createFile(bundledPath); + final var args = List.of(bundledPath.toString(), "-v", "error", "-select_streams", "v", "-show_entries", "stream=width,height,duration", "-of", "csv=p=0", in.toString()); + when(runner.run(args, Duration.ofSeconds(5))) + .thenReturn(result); + final var loader = new FFprobeVideoLoader(bundledPath, systemPath, runner); final var expectedInfo = new VideoInfoImpl("mp4", 1920, 1080, 25000L); final var expected = new FileVideoImpl(in, expectedInfo); - final var result = loader.loadVideo(in); - assertEquals(expected, result); - assertEquals("-v error -select_streams v -show_entries stream=width,height,duration -of csv=p=0 in.mp4", Files.readString(outputPath)); + final var actual = loader.loadVideo(in); + assertEquals(expected, actual); + verify(runner).run(args, Duration.ofSeconds(5)); + } + + @Test + void testLoadVideoSystem() throws IOException { + final var args = List.of(systemPath.toString(), "-v", "error", "-select_streams", "v", "-show_entries", "stream=width,height,duration", "-of", "csv=p=0", in.toString()); + when(runner.run(args, Duration.ofSeconds(5))).thenReturn(result); + final var loader = new FFprobeVideoLoader(Paths.get("bundled"), systemPath, runner); + final var expectedInfo = new VideoInfoImpl("mp4", 1920, 1080, 25000L); + final var expected = new FileVideoImpl(in, expectedInfo); + final var actual = loader.loadVideo(in); + assertEquals(expected, actual); + verify(runner).run(args, Duration.ofSeconds(5)); + } + + @Test + void testLoadVideoSystemError() throws IOException { + final var args = List.of(systemPath.toString(), "-v", "error", "-select_streams", "v", "-show_entries", "stream=width,height,duration", "-of", "csv=p=0", in.toString()); + when(runner.run(args, Duration.ofSeconds(5))).thenReturn(result); + final var loader = new FFprobeVideoLoader(Paths.get("bundled"), systemPath, runner); + when(result.exitCode()).thenReturn(1); + assertThrows(IOException.class, () -> loader.loadVideo(in)); + verify(runner).run(args, Duration.ofSeconds(5)); } } diff --git a/ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffmpeg.exe b/ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffmpeg.exe deleted file mode 100644 index 2bc1a2118a074cc459b77ec620273154899e3fd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25600 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 $Output -exit \ No newline at end of file diff --git a/ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffmpeg.sh b/ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffmpeg.sh deleted file mode 100644 index b6cbfe0..0000000 --- a/ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffmpeg.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -o errexit -o noclobber -o pipefail -o nounset - -output=/tmp/test-ffmpeg-output.txt - -echo "$@" >| $output \ No newline at end of file diff --git a/ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffprobe.exe b/ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffprobe.exe deleted file mode 100644 index 60c6ac71db68f6ee6759fdf2421e4ea0061cdd92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25600 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 $Output -Write-Host "1920,1080,25" -exit \ No newline at end of file diff --git a/ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffprobe.sh b/ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffprobe.sh deleted file mode 100644 index 438d99f..0000000 --- a/ffmpeg/src/test/resources/com/github/gtache/autosubtitle/ffmpeg/fake-ffprobe.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -set -o errexit -o noclobber -o pipefail -o nounset - -output=/tmp/test-ffprobe-output.txt - -echo "$@" >| $output -echo "1920,1080,25" \ No newline at end of file diff --git a/pom.xml b/pom.xml index a80a1a5..c12177c 100644 --- a/pom.xml +++ b/pom.xml @@ -192,6 +192,25 @@ + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + + + prepare-agent + + + + report + prepare-package + + report + + + + org.apache.maven.plugins maven-compiler-plugin @@ -221,6 +240,12 @@ + + + org.jacoco + jacoco-maven-plugin + + \ No newline at end of file diff --git a/whisper/common/src/main/java/com/github/gtache/autosubtitle/subtitle/extractor/whisper/AudioOrVideo.java b/whisper/common/src/main/java/com/github/gtache/autosubtitle/subtitle/extractor/whisper/AudioOrVideo.java index 6292431..d92be5c 100644 --- a/whisper/common/src/main/java/com/github/gtache/autosubtitle/subtitle/extractor/whisper/AudioOrVideo.java +++ b/whisper/common/src/main/java/com/github/gtache/autosubtitle/subtitle/extractor/whisper/AudioOrVideo.java @@ -33,6 +33,10 @@ public record AudioOrVideo(Audio audio, Video video) implements Audio { this(null, video); } + /** + * @param The type of the inner object + * @return The inner object (audio or video) + */ public T inner() { return (T) (audio == null ? video : audio); } diff --git a/whisper/common/src/test/java/com/github/gtache/autosubtitle/subtitle/extractor/whisper/TestAudioOrVideo.java b/whisper/common/src/test/java/com/github/gtache/autosubtitle/subtitle/extractor/whisper/TestAudioOrVideo.java new file mode 100644 index 0000000..e5bcabf --- /dev/null +++ b/whisper/common/src/test/java/com/github/gtache/autosubtitle/subtitle/extractor/whisper/TestAudioOrVideo.java @@ -0,0 +1,67 @@ +package com.github.gtache.autosubtitle.subtitle.extractor.whisper; + +import com.github.gtache.autosubtitle.Audio; +import com.github.gtache.autosubtitle.AudioInfo; +import com.github.gtache.autosubtitle.Video; +import com.github.gtache.autosubtitle.VideoInfo; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class TestAudioOrVideo { + + private final Audio audio; + private final Video video; + private final InputStream in; + + + TestAudioOrVideo(@Mock final Audio audio, @Mock final Video video, @Mock final InputStream in) { + this.audio = Objects.requireNonNull(audio); + this.video = Objects.requireNonNull(video); + this.in = Objects.requireNonNull(in); + } + + @Test + void testAudio() throws IOException { + final var audioOrVideo = new AudioOrVideo(audio); + when(audio.getInputStream()).thenReturn(in); + assertEquals(in, audioOrVideo.getInputStream()); + final var info = mock(AudioInfo.class); + when(audio.info()).thenReturn(info); + + assertEquals(info, audioOrVideo.info()); + assertEquals(audio, audioOrVideo.inner()); + } + + @Test + void testVideo() throws IOException { + final var audioOrVideo = new AudioOrVideo(video); + when(video.getInputStream()).thenReturn(in); + assertEquals(in, audioOrVideo.getInputStream()); + final var info = mock(VideoInfo.class); + when(video.info()).thenReturn(info); + + assertEquals(info, audioOrVideo.info()); + assertEquals(video, audioOrVideo.inner()); + } + + @Test + void testIllegal() { + assertThrows(NullPointerException.class, () -> new AudioOrVideo((Audio) null)); + assertThrows(NullPointerException.class, () -> new AudioOrVideo((Video) null)); + assertThrows(NullPointerException.class, () -> new AudioOrVideo(null, null)); + assertThrows(IllegalArgumentException.class, () -> new AudioOrVideo(audio, video)); + } + +}