diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5b4bd162..834e6bba 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,7 +26,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest, macos-latest, windows-latest ] - java-version: [ 8, 11, 14 ] + java-version: [ 8, 11, 17 ] fail-fast: false steps: diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index edd7531e..63b28654 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -4,7 +4,7 @@ me.qoomon maven-git-versioning-extension - 6.5.0 + 7.1.1 \ No newline at end of file diff --git a/README.md b/README.md index feebb22e..2949f63d 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Inspired by [ffmpeg-cli-wrapper](https://github.com/bramp/ffmpeg-cli-wrapper) **OS**: Ubuntu, MacOS, Windows -**JDK**: 8, 11, 14 +**JDK**: 8, 11, 17 # Usage @@ -533,10 +533,3 @@ will have Java 9 bytecode (version 53). ```shell mvn clean install -PJ9-module ``` - -## Unit Tests - -Some code in the unit tests relies on having ffmpeg / ffprobe available on your `PATH`. -Other parts of the unit test code requires that you have configured `FFMPEG_BIN` as either an environment variable or as a Java system property. - -You will need to ensure that both are set up correctly in order to run the unit tests successfully. \ No newline at end of file diff --git a/checkstyle.xml b/checkstyle.xml index 79bbdeca..8c6cf76b 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -112,6 +112,7 @@ + diff --git a/pom.xml b/pom.xml index 2d52da26..9b731e85 100644 --- a/pom.xml +++ b/pom.xml @@ -164,7 +164,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.3.0 + 3.3.1 8 @@ -251,7 +251,7 @@ com.puppycrawl.tools checkstyle - 8.45.1 + 9.0.1 @@ -266,7 +266,7 @@ org.apache.maven.plugins maven-pmd-plugin - 3.14.0 + 3.15.0 true true diff --git a/src/main/java/com/github/kokorin/jaffree/LogLevel.java b/src/main/java/com/github/kokorin/jaffree/LogLevel.java index 82f2bfec..ce5731be 100644 --- a/src/main/java/com/github/kokorin/jaffree/LogLevel.java +++ b/src/main/java/com/github/kokorin/jaffree/LogLevel.java @@ -94,6 +94,34 @@ public int code() { return code; } + /** + * Checks if passed in log level has the same or higher severity. + * + * @param other log level to compare with + * @return true if log level has the same or higher severity. + */ + public boolean isEqualOrHigher(final LogLevel other) { + return this.code() <= other.code(); + } + + /** + * Checks if this log level is {@link #INFO} or higher. + * + * @return true if {@link #INFO} or higher. + */ + public boolean isInfoOrHigher() { + return isEqualOrHigher(INFO); + } + + /** + * Checks if this log level is {@link #ERROR} or higher. + * + * @return true if {@link #ERROR} or higher. + */ + public boolean isErrorOrHigher() { + return isEqualOrHigher(ERROR); + } + /** * Returns LogLevel with specified code. * diff --git a/src/main/java/com/github/kokorin/jaffree/ffmpeg/BaseInput.java b/src/main/java/com/github/kokorin/jaffree/ffmpeg/BaseInput.java index 86d6da32..748ca2e1 100644 --- a/src/main/java/com/github/kokorin/jaffree/ffmpeg/BaseInput.java +++ b/src/main/java/com/github/kokorin/jaffree/ffmpeg/BaseInput.java @@ -44,7 +44,7 @@ public BaseInput(final String input) { * Set number of times input stream shall be looped. Loop 0 means no loop, * loop -1 means infinite loop. * - * @param streamLoop + * @param streamLoop number of loops * @return this */ @SuppressWarnings("checkstyle:hiddenfield") diff --git a/src/main/java/com/github/kokorin/jaffree/ffmpeg/ChannelInput.java b/src/main/java/com/github/kokorin/jaffree/ffmpeg/ChannelInput.java index 2b0564b9..d8fe0010 100644 --- a/src/main/java/com/github/kokorin/jaffree/ffmpeg/ChannelInput.java +++ b/src/main/java/com/github/kokorin/jaffree/ffmpeg/ChannelInput.java @@ -51,11 +51,10 @@ public ChannelInput(final String fileName, final SeekableByteChannel channel) { /** * Creates {@link ChannelInput}. - *

- * ffmpeg uses fileName's extension to autodetect input format * * @param channel byte channel * @return ChannelInput + * @see #fromChannel(String, SeekableByteChannel) */ public static ChannelInput fromChannel(final SeekableByteChannel channel) { return new ChannelInput(channel); diff --git a/src/main/java/com/github/kokorin/jaffree/ffmpeg/FFmpegResultFuture.java b/src/main/java/com/github/kokorin/jaffree/ffmpeg/FFmpegResultFuture.java index c18b2808..c71fdb8f 100644 --- a/src/main/java/com/github/kokorin/jaffree/ffmpeg/FFmpegResultFuture.java +++ b/src/main/java/com/github/kokorin/jaffree/ffmpeg/FFmpegResultFuture.java @@ -37,11 +37,11 @@ public class FFmpegResultFuture { /** * Creates {@link FFmpegResultFuture}. * - * @param resultFuture - * @param stopper + * @param resultFuture result future + * @param stopper stopper */ public FFmpegResultFuture(final CompletableFuture resultFuture, - final Stopper stopper) { + final Stopper stopper) { this.resultFuture = resultFuture; this.stopper = stopper; } @@ -144,9 +144,10 @@ public boolean isDone() { /** * Returns a completion that can be used to chain operations after FFmpeg completes, using the * {@link CompletionStage} Java 8 API. + * * @return completion that will complete when ffmpeg completes normally, and will complete - * exceptionally with a {@link CancellationException} if ffmpeg is forcefully stopped or with a - * {@link JaffreeException} if an error occurs. + * exceptionally with a {@link CancellationException} if ffmpeg is forcefully stopped or with a + * {@link JaffreeException} if an error occurs. */ public CompletableFuture toCompletableFuture() { return resultFuture; diff --git a/src/main/java/com/github/kokorin/jaffree/ffmpeg/FFmpegResultReader.java b/src/main/java/com/github/kokorin/jaffree/ffmpeg/FFmpegResultReader.java index b18e9511..5f9cc144 100644 --- a/src/main/java/com/github/kokorin/jaffree/ffmpeg/FFmpegResultReader.java +++ b/src/main/java/com/github/kokorin/jaffree/ffmpeg/FFmpegResultReader.java @@ -17,29 +17,17 @@ package com.github.kokorin.jaffree.ffmpeg; -import com.github.kokorin.jaffree.JaffreeException; -import com.github.kokorin.jaffree.LogLevel; import com.github.kokorin.jaffree.log.LogMessage; -import com.github.kokorin.jaffree.log.LogMessageIterator; -import com.github.kokorin.jaffree.process.StdReader; -import com.github.kokorin.jaffree.util.LineIterator; +import com.github.kokorin.jaffree.process.BaseStdReader; import com.github.kokorin.jaffree.util.ParseUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; /** * {@link FFmpegResultReader} reads ffmpeg stderr output, parses {@link FFmpegProgress} and * {@link FFmpegResult} and passes unparsed output to {@link OutputListener} (if provided). */ -public class FFmpegResultReader implements StdReader { +public class FFmpegResultReader extends BaseStdReader { private final OutputListener outputListener; - private static final Logger LOGGER = LoggerFactory.getLogger(FFmpegResultReader.class); - /** * Creates {@link FFmpegResultReader}. * @@ -50,92 +38,10 @@ public FFmpegResultReader(final OutputListener outputListener) { } /** - * Reads provided {@link InputStream} until it's depleted. - *

- * This method parses every line to detect ffmpeg log level. Lines with INFO log level are - * additionally parsed to get ffmpeg result (which may be null because of too strict log level) - * - * @param stdOut input stream to read from - * @return FFmpegResult if found or null - * @throws JaffreeException if IOException appears or ffmpeg ends with error message. - * @see FFmpeg#setLogLevel(LogLevel) + * {@inheritDoc} */ - @SuppressWarnings("checkstyle:MissingSwitchDefault") @Override - public FFmpegResult read(final InputStream stdOut) { - LogMessageIterator logMessageIterator = new LogMessageIterator( - new LineIterator( - new BufferedReader(new InputStreamReader(stdOut)) - ) - ); - - FFmpegResult result = null; - String errorMessage = null; - - while (logMessageIterator.hasNext()) { - LogMessage logMessage = logMessageIterator.next(); - - if (logMessage.logLevel != null) { - switch (logMessage.logLevel) { - case TRACE: - LOGGER.trace(logMessage.message); - break; - case VERBOSE: - case DEBUG: - LOGGER.debug(logMessage.message); - break; - case INFO: - LOGGER.info(logMessage.message); - break; - case WARNING: - LOGGER.warn(logMessage.message); - break; - case ERROR: - case FATAL: - case PANIC: - case QUIET: - LOGGER.error(logMessage.message); - break; - } - } else { - LOGGER.info(logMessage.message); - } - - if (logMessage.logLevel == LogLevel.INFO) { - FFmpegResult possibleResult = ParseUtil.parseResult(logMessage.message); - - if (possibleResult != null) { - result = possibleResult; - } - } - - if (outputListener != null && logMessage.logLevel != null - && logMessage.logLevel.code() <= LogLevel.INFO.code()) { - outputListener.onOutput(logMessage.message); - } - - if (logMessage.logLevel != null - && logMessage.logLevel.code() <= LogLevel.ERROR.code()) { - if (errorMessage == null) { - errorMessage = logMessage.message; - } else { - errorMessage += "\n" + logMessage.message; - } - } - } - - if (result != null) { - if (errorMessage != null) { - LOGGER.warn("One or more errors appeared during ffmpeg execution, " - + "ignoring since result is available"); - } - return result; - } - - if (errorMessage != null) { - throw new JaffreeException("ffmpeg exited with message: " + errorMessage); - } - + protected FFmpegResult defaultResult() { return new FFmpegResult( null, null, @@ -145,4 +51,17 @@ public FFmpegResult read(final InputStream stdOut) { null ); } + + /** + * {@inheritDoc} + */ + @Override + protected FFmpegResult handleLogMessage(final LogMessage logMessage) { + if (outputListener != null && logMessage.logLevel != null + && logMessage.logLevel.isInfoOrHigher()) { + outputListener.onOutput(logMessage.message); + } + + return ParseUtil.parseResult(logMessage.message); + } } diff --git a/src/main/java/com/github/kokorin/jaffree/ffprobe/FFprobeLogReader.java b/src/main/java/com/github/kokorin/jaffree/ffprobe/FFprobeLogReader.java index 61fb76dd..05be83f3 100644 --- a/src/main/java/com/github/kokorin/jaffree/ffprobe/FFprobeLogReader.java +++ b/src/main/java/com/github/kokorin/jaffree/ffprobe/FFprobeLogReader.java @@ -17,85 +17,22 @@ package com.github.kokorin.jaffree.ffprobe; -import com.github.kokorin.jaffree.JaffreeException; -import com.github.kokorin.jaffree.LogLevel; import com.github.kokorin.jaffree.log.LogMessage; -import com.github.kokorin.jaffree.log.LogMessageIterator; -import com.github.kokorin.jaffree.process.StdReader; -import com.github.kokorin.jaffree.util.LineIterator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; +import com.github.kokorin.jaffree.process.BaseStdReader; /** - * {@link FFprobeLogReader} reads ffprobe output (from stderr), parses {@link FFprobeResult} and - * logs and sends logs to SLF4J with corresponding log level. + * {@link FFprobeLogReader} reads ffprobe output (from stderr), parses logs and sends logs + * to SLF4J with corresponding log level. */ -public class FFprobeLogReader implements StdReader { - private static final Logger LOGGER = LoggerFactory.getLogger(FFprobeLogReader.class); - +public class FFprobeLogReader extends BaseStdReader { /** - * Reads ffprobe output from provided {@link InputStream} and parses {@link FFprobeResult} and - * logs. - * @param inputStream input stream to read from - * @return parsed {@link FFprobeResult} + * Does nothing as ffprobe prints logs and result in different output streams. + * + * @param logMessage log message + * @return null */ - @SuppressWarnings("checkstyle:MissingSwitchDefault") @Override - public FFprobeResult read(final InputStream inputStream) { - LogMessageIterator logMessageIterator = new LogMessageIterator( - new LineIterator( - new BufferedReader(new InputStreamReader(inputStream)) - ) - ); - - String errorMessage = null; - while (logMessageIterator.hasNext()) { - LogMessage logMessage = logMessageIterator.next(); - - if (logMessage.logLevel != null) { - switch (logMessage.logLevel) { - case TRACE: - LOGGER.trace(logMessage.message); - break; - case VERBOSE: - case DEBUG: - LOGGER.debug(logMessage.message); - break; - case INFO: - LOGGER.info(logMessage.message); - break; - case WARNING: - LOGGER.warn(logMessage.message); - break; - case ERROR: - case FATAL: - case PANIC: - case QUIET: - LOGGER.error(logMessage.message); - break; - } - } else { - LOGGER.info(logMessage.message); - } - - if (logMessage.logLevel != null - && logMessage.logLevel.code() <= LogLevel.ERROR.code()) { - if (errorMessage == null) { - errorMessage = logMessage.message; - } else { - errorMessage += "\n" + logMessage.message; - } - } - } - - if (errorMessage != null) { - throw new JaffreeException("Finished with error message: " + errorMessage); - } - + protected FFprobeResult handleLogMessage(final LogMessage logMessage) { return null; } } diff --git a/src/main/java/com/github/kokorin/jaffree/net/FtpServer.java b/src/main/java/com/github/kokorin/jaffree/net/FtpServer.java index 7b5dcaec..ba1f834d 100644 --- a/src/main/java/com/github/kokorin/jaffree/net/FtpServer.java +++ b/src/main/java/com/github/kokorin/jaffree/net/FtpServer.java @@ -83,6 +83,8 @@ protected void serve(final Socket controlServerSocket) throws IOException { OutputStream controlOutput = controlServerSocket.getOutputStream()) { operate(controlReader, controlOutput, dataServerSocket); + } catch (IOException e) { + LOGGER.debug("Connection closed: {}", e.getMessage()); } catch (Exception e) { throw new JaffreeException("Failed to serve FTP", e); } @@ -105,11 +107,13 @@ protected void operate(final BufferedReader controlReader, boolean quit = false; while (!quit) { String line = controlReader.readLine(); + if (line == null) { - LOGGER.debug("Closing control connection"); break; } + LOGGER.debug("Received command: {}", line); + String[] commandAndArgs = line.split(" ", 2); String command = commandAndArgs[0].toUpperCase(); String args = null; @@ -117,8 +121,6 @@ protected void operate(final BufferedReader controlReader, args = commandAndArgs[1]; } - LOGGER.debug("Received command: {}", line); - switch (command) { case "USER": doUser(controlOutput, args); @@ -148,7 +150,8 @@ protected void operate(final BufferedReader controlReader, doAbor(controlOutput); break; case "FEAT": - // intentional fall through + doFeat(controlOutput); + break; case "EPSV": doNotImplemented(controlOutput); break; @@ -283,12 +286,15 @@ private void doRetr(final OutputStream output, final ServerSocket dataServerSock OutputStream dataOutput = dataSocket.getOutputStream()) { LOGGER.debug("Data connection established: {}", dataSocket); copied = IOUtil.copy(Channels.newInputStream(channel), dataOutput, buffer); + dataOutput.flush(); LOGGER.debug("Copied {} bytes to data socket", copied); + println(output, "226 Operation successful"); } catch (SocketException e) { // ffmpeg can close data connection without fully reading requested data. // This is not an error and should be ignored. // FTP server should serve further requests sent via Control connection LOGGER.debug("Data connection error ignored (RETR): {}", e.getMessage()); + println(output, "426 TCP connection broken"); } } @@ -311,8 +317,10 @@ private void doStor(final OutputStream output, final ServerSocket dataServerSock LOGGER.debug("Data connection established: {}", dataSocket); copied = IOUtil.copy(dataInput, Channels.newOutputStream(channel), buffer); LOGGER.debug("Copied {} bytes from data socket", copied); + println(output, "226 Operation successful"); } catch (SocketException e) { LOGGER.info("Data connection error ignored (STOR): {}", e.getMessage()); + println(output, "426 TCP connection broken"); } } @@ -326,6 +334,16 @@ private void doAbor(final OutputStream output) throws IOException { println(output, "226 Closing data connection."); } + /** + * Sends response to FEAT (features) control command. + * + * @param output output to write response + * @throws IOException socket IO exception + */ + private void doFeat(final OutputStream output) throws IOException { + println(output, "211 No features."); + } + /** * Sends response to non-implemented control commands. * @@ -340,6 +358,7 @@ private void println(final OutputStream output, final String line) throws IOExce LOGGER.debug("Responding: {}", line); output.write(line.getBytes()); output.write(NEW_LINE); + output.flush(); } /** diff --git a/src/main/java/com/github/kokorin/jaffree/process/BaseStdReader.java b/src/main/java/com/github/kokorin/jaffree/process/BaseStdReader.java new file mode 100644 index 00000000..5d390811 --- /dev/null +++ b/src/main/java/com/github/kokorin/jaffree/process/BaseStdReader.java @@ -0,0 +1,116 @@ +/* + * Copyright 2021 Denis Kokorin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.github.kokorin.jaffree.process; + +import com.github.kokorin.jaffree.JaffreeException; +import com.github.kokorin.jaffree.LogLevel; +import com.github.kokorin.jaffree.log.LogMessage; +import com.github.kokorin.jaffree.log.LogMessageIterator; +import com.github.kokorin.jaffree.util.LineIterator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * {@link BaseStdReader} reads std output, parses result and logs, and sends logs + * to SLF4J with corresponding log level. + * + * @param type of parsed result + */ +public abstract class BaseStdReader implements StdReader { + private static final Logger LOGGER = LoggerFactory.getLogger(BaseStdReader.class); + + /** + * Reads provided {@link InputStream} until it's depleted. + *

+ * This method parses every line to detect ffmpeg log level. Lines with INFO log level are + * additionally parsed for a result (which may be null because of too strict log level). + * + * @param stdOut input stream to read from + * @return T if found or null + * @throws JaffreeException if IOException appears or program ends with error message. + * @see com.github.kokorin.jaffree.ffmpeg.FFmpeg#setLogLevel(LogLevel) + * @see com.github.kokorin.jaffree.ffprobe.FFprobe#setLogLevel(LogLevel) + */ + @SuppressWarnings("checkstyle:MissingSwitchDefault") + @Override + public T read(final InputStream stdOut) { + LogMessageIterator logMessageIterator = new LogMessageIterator( + new LineIterator( + new BufferedReader(new InputStreamReader(stdOut)) + ) + ); + + T result = defaultResult(); + + while (logMessageIterator.hasNext()) { + LogMessage logMessage = logMessageIterator.next(); + + if (logMessage.logLevel != null) { + switch (logMessage.logLevel) { + case TRACE: + LOGGER.trace(logMessage.message); + break; + case VERBOSE: + case DEBUG: + LOGGER.debug(logMessage.message); + break; + case INFO: + LOGGER.info(logMessage.message); + break; + case WARNING: + LOGGER.warn(logMessage.message); + break; + case ERROR: + case FATAL: + case PANIC: + case QUIET: + LOGGER.error(logMessage.message); + break; + } + } else { + LOGGER.info(logMessage.message); + } + + T possibleResult = handleLogMessage(logMessage); + if (possibleResult != null) { + result = possibleResult; + } + } + + return result; + } + + /** + * @return default result + */ + protected T defaultResult() { + return null; + } + + /** + * Parses single ffmpeg/ffprobe log message. + * + * @param logMessage log message + * @return parsed result or null + */ + protected abstract T handleLogMessage(LogMessage logMessage); +} diff --git a/src/main/java/com/github/kokorin/jaffree/process/GobblingStdReader.java b/src/main/java/com/github/kokorin/jaffree/process/GobblingStdReader.java index 3eb44688..1819ff09 100644 --- a/src/main/java/com/github/kokorin/jaffree/process/GobblingStdReader.java +++ b/src/main/java/com/github/kokorin/jaffree/process/GobblingStdReader.java @@ -27,7 +27,7 @@ /** * {@link StdReader} implementation which reads and ignores bytes read. * - * @param + * @param type of parsed result */ public class GobblingStdReader implements StdReader { diff --git a/src/main/java/com/github/kokorin/jaffree/process/LoggingStdReader.java b/src/main/java/com/github/kokorin/jaffree/process/LoggingStdReader.java index a4c74b43..7da5ba00 100644 --- a/src/main/java/com/github/kokorin/jaffree/process/LoggingStdReader.java +++ b/src/main/java/com/github/kokorin/jaffree/process/LoggingStdReader.java @@ -29,7 +29,7 @@ /** * {@link StdReader} implementation which reads and logs everything been read. * - * @param + * @param type of parsed result */ public class LoggingStdReader implements StdReader { private static final Logger LOGGER = LoggerFactory.getLogger(LoggingStdReader.class); diff --git a/src/main/java/com/github/kokorin/jaffree/process/ProcessHandler.java b/src/main/java/com/github/kokorin/jaffree/process/ProcessHandler.java index 6b26bc4d..9c7bc5ee 100644 --- a/src/main/java/com/github/kokorin/jaffree/process/ProcessHandler.java +++ b/src/main/java/com/github/kokorin/jaffree/process/ProcessHandler.java @@ -24,6 +24,7 @@ import java.io.Closeable; import java.io.IOException; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -145,10 +146,10 @@ public synchronized T execute() { return interactWithProcess(process); } catch (IOException e) { + collectDebugInformation(); throw new JaffreeException("Failed to start process.", e); } finally { if (process != null) { - // TODO on Windows process sometimes doesn't stop and keeps running process.destroy(); // Process must be destroyed before closing streams, can't use // try-with-resources, as resources are closing when leaving try block, @@ -199,7 +200,9 @@ protected T interactWithProcess(final Process process) { if (!Integer.valueOf(0).equals(status)) { throw new JaffreeException( - "Process execution has ended with non-zero status: " + status); + "Process execution has ended with non-zero status: " + status + + ". Check logs for detailed error message." + ); } T result = resultRef.get(); @@ -318,4 +321,17 @@ private static void waitForExecutorToStop(final Executor executor, final long ti Thread.sleep(100); } while (executor.isRunning()); } + + private static void collectDebugInformation() { + try { + LOGGER.warn("Collecting debug information"); + LOGGER.warn("User: {}", System.getProperty("user.name")); + LOGGER.warn("OS: {}", System.getProperty("os.name")); + LOGGER.warn("User Dir: {}", System.getProperty("user.dir")); + LOGGER.warn("Work Dir: {}", Paths.get(".").toAbsolutePath()); + LOGGER.warn("PATH: {}", System.getenv("PATH")); + } catch (Exception e) { + LOGGER.warn("Failure while collecting debug information.", e); + } + } } diff --git a/src/test/java/com/github/kokorin/jaffree/LogLevelTest.java b/src/test/java/com/github/kokorin/jaffree/LogLevelTest.java new file mode 100644 index 00000000..a1ba987d --- /dev/null +++ b/src/test/java/com/github/kokorin/jaffree/LogLevelTest.java @@ -0,0 +1,34 @@ +package com.github.kokorin.jaffree; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class LogLevelTest { + + @Test + public void testIsEqualOrHigher() { + assertTrue(LogLevel.INFO.isEqualOrHigher(LogLevel.DEBUG)); + assertTrue(LogLevel.ERROR.isEqualOrHigher(LogLevel.ERROR)); + assertFalse(LogLevel.DEBUG.isEqualOrHigher(LogLevel.INFO)); + } + + @Test + public void testIsInfoOrHigher() { + assertTrue(LogLevel.INFO.isInfoOrHigher()); + assertTrue(LogLevel.ERROR.isInfoOrHigher()); + assertTrue(LogLevel.FATAL.isInfoOrHigher()); + assertTrue(LogLevel.PANIC.isInfoOrHigher()); + assertFalse(LogLevel.DEBUG.isInfoOrHigher()); + } + + @Test + public void testIsErrorOrHigher() { + assertTrue(LogLevel.ERROR.isErrorOrHigher()); + assertTrue(LogLevel.FATAL.isErrorOrHigher()); + assertTrue(LogLevel.PANIC.isErrorOrHigher()); + assertFalse(LogLevel.INFO.isErrorOrHigher()); + assertFalse(LogLevel.DEBUG.isErrorOrHigher()); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java b/src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java index 950a3a9c..29f631d2 100644 --- a/src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java +++ b/src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java @@ -8,6 +8,7 @@ import com.github.kokorin.jaffree.ffprobe.FFprobe; import com.github.kokorin.jaffree.ffprobe.FFprobeResult; import com.github.kokorin.jaffree.ffprobe.Stream; +import com.github.kokorin.jaffree.process.ProcessHandler; import com.github.kokorin.jaffree.process.ProcessHelper; import org.hamcrest.core.AllOf; import org.hamcrest.core.StringContains; @@ -209,7 +210,8 @@ public void testDuration() throws Exception { @Test public void testForceStopWithProgressListenerException() throws Exception { - expectedException.expect(new StackTraceMatcher("Stop ffmpeg with ProgressListener Exception")); + expectedException.expect( + new StackTraceMatcher("Stop ffmpeg with ProgressListener Exception")); Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(Artifacts.VIDEO_MP4.getFileName()); @@ -493,7 +495,9 @@ public void testMap() throws Exception { @Test public void testExceptionIsThrownIfFfmpegExitsWithError() { - expectedException.expect(new StackTraceMatcher("No such file or directory")); + expectedException.expect( + new StackTraceMatcher("Process execution has ended with non-zero status") + ); FFmpegResult result = FFmpeg.atPath(Config.FFMPEG_BIN) .addInput(UrlInput.fromPath(ERROR_MP4)) @@ -717,7 +721,8 @@ public void testChannelOutput() throws IOException { LOGGER.debug("Will write to " + outputPath); - try (SeekableByteChannel channel = Files.newByteChannel(outputPath, CREATE, WRITE, READ, TRUNCATE_EXISTING)) { + try (SeekableByteChannel channel = Files.newByteChannel(outputPath, CREATE, WRITE, READ, + TRUNCATE_EXISTING)) { FFmpegResult result = FFmpeg.atPath(Config.FFMPEG_BIN) .addInput( UrlInput.fromPath(Artifacts.VIDEO_MP4) @@ -902,4 +907,13 @@ public void testAsyncToCompletableFuture() throws Exception { assertEquals(2, probeResult.getStreams().size()); } + + @Test + @Ignore("Should be ran manually") + public void testNoFFmpegExecutableFound() { + FFmpeg.atPath(Paths.get(".")) + .addInput(UrlInput.fromPath(Artifacts.VIDEO_MP4)) + .addOutput(new NullOutput()) + .execute(); + } } diff --git a/src/test/java/com/github/kokorin/jaffree/ffprobe/FFprobeTest.java b/src/test/java/com/github/kokorin/jaffree/ffprobe/FFprobeTest.java index 8115dd5c..2e223eba 100644 --- a/src/test/java/com/github/kokorin/jaffree/ffprobe/FFprobeTest.java +++ b/src/test/java/com/github/kokorin/jaffree/ffprobe/FFprobeTest.java @@ -684,7 +684,9 @@ public void testPacketSideDataListAttributes() throws Exception { @Test public void testExceptionIsThrownIfFfprobeExitsWithError() { - expectedException.expect(new StackTraceMatcher("No such file or directory")); + expectedException.expect( + new StackTraceMatcher("Process execution has ended with non-zero status") + ); FFprobeResult result = FFprobe.atPath(Config.FFMPEG_BIN) .setInput(Paths.get("nonexistent.mp4")) @@ -827,7 +829,9 @@ public void testAsyncExecution() throws Exception { @Test public void testAsyncExecutionWithException() throws Exception { - expectedException.expect(new StackTraceMatcher("No such file or directory")); + expectedException.expect( + new StackTraceMatcher("Process execution has ended with non-zero status") + ); FFprobeResult result = FFprobe.atPath(Config.FFMPEG_BIN) .setShowStreams(true)