Skip to content

Commit

Permalink
take out just the image and allow for just raw argument and add tabs …
Browse files Browse the repository at this point in the history
…with 8 spaces
  • Loading branch information
kclauson authored and mjeanroy committed Dec 21, 2024
1 parent b701527 commit 51646a9
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 193 deletions.
28 changes: 7 additions & 21 deletions src/main/java/com/thebuzzmedia/exiftool/ExifTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -527,36 +527,22 @@ private Map<Tag, String> getImageMeta(File image, Collection<? extends Tag> tags
}

/**
* Run users custom Exiftool command on image and returns raw output from Exiftool as string
* Run user's custom Exiftool command and returns raw output from Exiftool as string
* This just passes the arguments to Exiftool and does not do any checking on the validity of
* those arguments before passing them to Exiftool. The user is also responsible for parsing
* those arguments or validity of any image file in the argument before passing them to
* Exiftool. The user is also responsible for parsing
* the raw output that has been returned.
*
* @param image Image.
* @param arguments List of strings containing the commands to pass to exiftool
* @return String with whatever exiftool outputs.
* @throws IOException If something bad happen during I/O operations.
* @throws NullPointerException If one parameter is null.
* @throws com.thebuzzmedia.exiftool.exceptions.UnreadableFileException If image cannot be read.
* @throws NullPointerException If parameter is null.
*/
public String getRawExifToolOutput(File image, List<String> arguments) throws IOException {
requireNonNull(image, "Image cannot be null and must be a valid stream of image data.");
public String getRawExifToolOutput(List<String> arguments) throws IOException {
requireNonNull(arguments, "Arguments cannot be null.");
isReadable(image, String.format("Unable to read the given image [%s], ensure that the image exists at the given withPath and that the executing Java process has permissions to read it.", image));

int expectedSize = arguments.size() + 2;
List<String> args = new ArrayList<>(expectedSize);

addAll(args, arguments);

// Add image argument.
args.add(image.getAbsolutePath());

// Add last argument.
// This argument will only be used by exiftool if stay_open flag has been set.
args.add("-execute");

RawOutputHandler resultHandler = new RawOutputHandler();
strategy.execute(executor, path, args, resultHandler);
strategy.execute(executor, path, arguments, resultHandler);

return resultHandler.getOutput();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,31 @@

public class RawOutputHandler implements OutputHandler {

private final StringBuilder output;

public RawOutputHandler() {
this.output = new StringBuilder();
}

@Override
public boolean readLine(String line) {
// If line is null, then this is the end.
// If line is strictly equals to "{ready}", then it means that stay_open feature
// is enabled and this is the end of the output.
if (!stopHandler().readLine(line)) {
return false;
}

if (output.length() > 0) {
output.append(Constants.BR);
}
output.append(line);

return true;
}

public String getOutput() {
return output.toString();
}
private final StringBuilder output;

public RawOutputHandler() {
this.output = new StringBuilder();
}

@Override
public boolean readLine(String line) {
// If line is null, then this is the end.
// If line is strictly equals to "{ready}", then it means that stay_open feature
// is enabled and this is the end of the output.
if (!stopHandler().readLine(line)) {
return false;
}

if (output.length() > 0) {
output.append(Constants.BR);
}
output.append(line);

return true;
}

public String getOutput() {
// output the raw string that exiftool outputes
return output.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,118 +30,93 @@
import org.mockito.stubbing.Answer;

public class ExifTool_getRawExifToolOutput_Test {
private String path;
private CommandExecutor executor;
private ExecutionStrategy strategy;
private List<String> args;

private ExifTool exifTool;

@BeforeEach
void setUp() throws Exception {
executor = mock(CommandExecutor.class);
strategy = mock(ExecutionStrategy.class);
path = "exiftool";
args = asList("-a", "-u", "-g1", "-j");
CommandResult cmd = new CommandResultBuilder().output("9.36").build();
when(executor.execute(any(Command.class))).thenReturn(cmd);
when(strategy.isSupported(any(Version.class))).thenReturn(true);

exifTool = new ExifTool(path, executor, strategy);

reset(executor);
}

@Test
void it_should_fail_if_image_is_null() {
assertThatThrownBy(() -> exifTool.getRawExifToolOutput(null, args))
.isInstanceOf(NullPointerException.class)
.hasMessage("Image cannot be null and must be a valid stream of image data.");
}

@Test
void it_should_fail_if_arguments_is_null() {
assertThatThrownBy(() -> exifTool.getRawExifToolOutput(mock(File.class), null))
.isInstanceOf(NullPointerException.class)
.hasMessage("Arguments cannot be null.");
}

@Test
void it_should_fail_with_unknown_file() {
File image = new FileBuilder("foo.png").exists(false).build();
assertThatThrownBy(() -> exifTool.getRawExifToolOutput(image, args))
.isInstanceOf(UnreadableFileException.class)
.hasMessage(
"Unable to read the given image [/tmp/foo.png], " +
"ensure that the image exists at the given withPath and that the " +
"executing Java process has permissions to read it."
);
}

@Test
@SuppressWarnings("unchecked")
void it_should_get_raw_image_metadata() throws Exception {
// Given
File image = new FileBuilder("foo.png").build();

String rawOutput = "[{\n"
+ "\"SourceFile\": \"tmp/foo.png\",\n"
+ "\"IFD0\": {\n"
+ "\"Make\": \"HTC\",\n"
+ "\"Model\": \"myTouch 4G\",\n"
+ "\"XResolution\": 72,\n"
+ "\"YResolution\": 72,\n"
+ "\"ResolutionUnit\": \"inches\",\n"
+ "\"YCbCrPositioning\": \"Centered\"\n"
+ "},\n"
+ "}]";

doAnswer(new ReadRawOutputAnswer(rawOutput, "{ready}")).when(strategy).execute(
same(executor), same(path), anyListOf(String.class), any(OutputHandler.class)
);

// When
String result = exifTool.getRawExifToolOutput(image, args);

// Then
ArgumentCaptor<List<String>> argsCaptor = ArgumentCaptor.forClass(List.class);
verify(strategy).execute(same(executor), same(path), argsCaptor.capture(), any(OutputHandler.class));

List<String> arguments = argsCaptor.getValue();
assertThat(arguments).isNotEmpty().containsExactly(
"-a",
"-u",
"-g1",
"-j",
"/tmp/foo.png",
"-execute"
);
assertThat(result).isEqualTo("hello");
}

private static final class ReadRawOutputAnswer implements Answer<Void> {
private final String rawOutput;

private final String end;

private ReadRawOutputAnswer(String rawOutput, String end) {
this.rawOutput = rawOutput;
this.end = end;
}

@Override
public Void answer(InvocationOnMock invocation) {
OutputHandler handler = (OutputHandler) invocation.getArguments()[3];
String[]lines = rawOutput.split(System.getProperty("line.separator"));
// read raw output
for(String tmpLine : lines){
handler.readLine(tmpLine);
}

// Read last line
handler.readLine(end);

return null;
}
}
private String path;
private CommandExecutor executor;
private ExecutionStrategy strategy;
private List<String> args;
private ExifTool exifTool;

@BeforeEach
void setUp() throws Exception {
executor = mock(CommandExecutor.class);
strategy = mock(ExecutionStrategy.class);
path = "exiftool";
args = asList("-a", "-u", "-g1", "-j", "/tmp/foo.png", "-execute");
CommandResult cmd = new CommandResultBuilder().output("9.36").build();
when(executor.execute(any(Command.class))).thenReturn(cmd);
when(strategy.isSupported(any(Version.class))).thenReturn(true);

exifTool = new ExifTool(path, executor, strategy);

reset(executor);
}

@Test
void it_should_fail_if_arguments_is_null() {
assertThatThrownBy(() -> exifTool.getRawExifToolOutput(null))
.isInstanceOf(NullPointerException.class)
.hasMessage("Arguments cannot be null.");
}

@Test
void it_should_get_raw_image_metadata() throws Exception {
String rawOutput = "[{\n"
+ "\"SourceFile\": \"tmp/foo.png\",\n"
+ "\"IFD0\": {\n"
+ "\"Make\": \"HTC\",\n"
+ "\"Model\": \"myTouch 4G\",\n"
+ "\"XResolution\": 72,\n"
+ "\"YResolution\": 72,\n"
+ "\"ResolutionUnit\": \"inches\",\n"
+ "\"YCbCrPositioning\": \"Centered\"\n"
+ "},\n"
+ "}]";

doAnswer(new ReadRawOutputAnswer(rawOutput, "{ready}")).when(strategy).execute(
same(executor), same(path), anyListOf(String.class), any(OutputHandler.class)
);

// When
String result = exifTool.getRawExifToolOutput(args);

// Then
ArgumentCaptor<List<String>> argsCaptor = ArgumentCaptor.forClass(List.class);
verify(strategy).execute(same(executor), same(path), argsCaptor.capture(), any(OutputHandler.class));

List<String> arguments = argsCaptor.getValue();
assertThat(arguments).isNotEmpty().containsExactly(
"-a",
"-u",
"-g1",
"-j",
"/tmp/foo.png",
"-execute"
);
assertThat(result).isEqualTo(rawOutput);
}

private static final class ReadRawOutputAnswer implements Answer<Void> {
private final String rawOutput;
private final String end;

private ReadRawOutputAnswer(String rawOutput, String end) {
this.rawOutput = rawOutput;
this.end = end;
}

@Override
public Void answer(InvocationOnMock invocation) {
OutputHandler handler = (OutputHandler) invocation.getArguments()[3];
String[]lines = rawOutput.split(System.getProperty("line.separator"));
// read raw output
for(String tmpLine : lines){
handler.readLine(tmpLine);
}

// Read last line
handler.readLine(end);

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,35 @@

public class RawOutputHandlerTest {

@Test
void it_should_read_null_line() {
RawOutputHandler handler = new RawOutputHandler();
boolean hasNext = handler.readLine(null);
assertThat(hasNext).isFalse();
assertThat(handler.getOutput()).isNotNull().isEmpty();
}

@Test
void it_should_read_last_line() {
RawOutputHandler handler = new RawOutputHandler();
boolean hasNext = handler.readLine("{ready}");
assertThat(hasNext).isFalse();
assertThat(handler.getOutput()).isNotNull().isEmpty();
}

@Test
void it_should_read_line() {
String rawOutput = "[{\"SourceFile\": \"tmp/foo.png\",\"IFD0\": {\"Make\": \"HTC\","
+ "\"Model\": \"myTouch 4G\", \"XResolution\": 72, \"YResolution\": 72,"
+ "\"ResolutionUnit\": \"inches\", \"YCbCrPositioning\": \"Centered\"},}]";

RawOutputHandler handler = new RawOutputHandler();
boolean hasNext = handler.readLine(rawOutput);

String result = handler.getOutput();
assertThat(hasNext).isTrue();
assertThat(result).hasSize(rawOutput.length());

assertThat(result).isEqualTo(rawOutput);
}

@Test
void it_should_read_null_line() {
RawOutputHandler handler = new RawOutputHandler();
boolean hasNext = handler.readLine(null);
assertThat(hasNext).isFalse();
assertThat(handler.getOutput()).isNotNull().isEmpty();
}

@Test
void it_should_read_last_line() {
RawOutputHandler handler = new RawOutputHandler();
boolean hasNext = handler.readLine("{ready}");
assertThat(hasNext).isFalse();
assertThat(handler.getOutput()).isNotNull().isEmpty();
}

@Test
void it_should_read_line() {
String rawOutput = "[{\"SourceFile\": \"tmp/foo.jpg\",\"IFD0\": {\"Make\": \"HTC\","
+ "\"Model\": \"myTouch 4G\", \"XResolution\": 72, \"YResolution\": 72,"
+ "\"ResolutionUnit\": \"inches\", \"YCbCrPositioning\": \"Centered\"},}]";

RawOutputHandler handler = new RawOutputHandler();
boolean hasNext = handler.readLine(rawOutput);

String result = handler.getOutput();
assertThat(hasNext).isTrue();
assertThat(result).hasSize(rawOutput.length());

assertThat(result).isEqualTo(rawOutput);
}
}

0 comments on commit 51646a9

Please sign in to comment.