Adds tests for FFmpeg

This commit is contained in:
Guillaume Tâche
2024-09-21 21:22:30 +02:00
parent 703a4c71ae
commit 7f99c48e2c
13 changed files with 448 additions and 102 deletions

View File

@@ -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<? extends SubtitleCollection<?>> 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<String> args, final Duration duration) throws IOException {
private void runLog(final List<String> 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());
}
}
}

View File

@@ -37,6 +37,7 @@ 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));
if (result.exitCode() == 0) {
final var resolution = result.output().getLast();
final var split = resolution.split(",");
final var width = Integer.parseInt(split[0]);
@@ -46,6 +47,9 @@ public class FFprobeVideoLoader implements VideoLoader {
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() {

View File

@@ -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<Subtitle> 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<Subtitle> 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<Integer, String>();
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<Integer, String> argMap, final List<String> 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<Integer, String>();
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<Integer, String>();
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<Integer, String>();
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<Integer, String>();
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<Integer, String>();
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<String> getArgs() throws IOException {
final var captor = ArgumentCaptor.forClass(List.class);
verify(runner).startListen(captor.capture());
return captor.getValue();
}
}

View File

@@ -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));
}
}

View File

@@ -1,5 +0,0 @@
$TempDir = [System.IO.Path]::GetTempPath()
$Output = "$TempDir\test-ffmpeg-output.txt"
Write-Output "$args" > $Output
exit

View File

@@ -1,6 +0,0 @@
#!/bin/bash
set -o errexit -o noclobber -o pipefail -o nounset
output=/tmp/test-ffmpeg-output.txt
echo "$@" >| $output

View File

@@ -1,6 +0,0 @@
$TempDir = [System.IO.Path]::GetTempPath()
$Output = "$TempDir\test-ffprobe-output.txt"
Write-Output "$args" > $Output
Write-Host "1920,1080,25"
exit

View File

@@ -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"

25
pom.xml
View File

@@ -192,6 +192,25 @@
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
@@ -221,6 +240,12 @@
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -33,6 +33,10 @@ public record AudioOrVideo(Audio audio, Video video) implements Audio {
this(null, video);
}
/**
* @param <T> The type of the inner object
* @return The inner object (audio or video)
*/
public <T> T inner() {
return (T) (audio == null ? video : audio);
}

View File

@@ -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));
}
}