diff --git a/build.gradle b/build.gradle index 412e93e..7801d2d 100644 --- a/build.gradle +++ b/build.gradle @@ -90,3 +90,11 @@ task updateBuildVersion << { file('version.txt').text = "${updatedVersion}\n" } } + +task copyConfig(type: Copy) { + + from 'conf/app/application.${env}.properties' + from 'conf/app/log4j2.${env}.xml' + + to 'src/main/resources/' +} diff --git a/conf/app/application.local.properties b/conf/app/application.local.properties new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/application.properties b/conf/app/application.release.properties similarity index 100% rename from src/main/resources/application.properties rename to conf/app/application.release.properties diff --git a/src/main/resources/log4j2.xml b/conf/logging/log4j2.local.xml similarity index 95% rename from src/main/resources/log4j2.xml rename to conf/logging/log4j2.local.xml index 24bb5bd..6af558c 100644 --- a/src/main/resources/log4j2.xml +++ b/conf/logging/log4j2.local.xml @@ -28,7 +28,6 @@ - diff --git a/conf/logging/log4j2.release.xml b/conf/logging/log4j2.release.xml new file mode 100644 index 0000000..4f7aab0 --- /dev/null +++ b/conf/logging/log4j2.release.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} - $5p - [%c{1}] - %msg%n + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/io/linuxserver/davos/transfer/ftp/FTPFile.java b/src/main/java/io/linuxserver/davos/transfer/ftp/FTPFile.java index 56c3f64..36dd3bf 100644 --- a/src/main/java/io/linuxserver/davos/transfer/ftp/FTPFile.java +++ b/src/main/java/io/linuxserver/davos/transfer/ftp/FTPFile.java @@ -6,15 +6,15 @@ public class FTPFile { private String name; private long size; - private String absolutePath; + private String path; private DateTime lastModified; private boolean directory; - public FTPFile(String name, long size, String absolutePath, long mTime, boolean directory) { + public FTPFile(String name, long size, String path, long mTime, boolean directory) { this.name = name; this.size = size; - this.absolutePath = absolutePath; + this.path = path; this.lastModified = new DateTime(mTime); this.directory = directory; } @@ -28,7 +28,7 @@ public class FTPFile { } public String getPath() { - return absolutePath; + return path; } public DateTime getLastModified() { diff --git a/src/main/java/io/linuxserver/davos/transfer/ftp/client/FTPSClient.java b/src/main/java/io/linuxserver/davos/transfer/ftp/client/FTPSClient.java new file mode 100644 index 0000000..748aab9 --- /dev/null +++ b/src/main/java/io/linuxserver/davos/transfer/ftp/client/FTPSClient.java @@ -0,0 +1,8 @@ +package io.linuxserver.davos.transfer.ftp.client; + +public class FTPSClient extends FTPClient { + + public FTPSClient() { + ftpClient = new org.apache.commons.net.ftp.FTPSClient("SSL", true); + } +} diff --git a/src/main/java/io/linuxserver/davos/transfer/ftp/connection/FTPConnection.java b/src/main/java/io/linuxserver/davos/transfer/ftp/connection/FTPConnection.java index 43cd8ea..19f5eaa 100644 --- a/src/main/java/io/linuxserver/davos/transfer/ftp/connection/FTPConnection.java +++ b/src/main/java/io/linuxserver/davos/transfer/ftp/connection/FTPConnection.java @@ -1,37 +1,141 @@ package io.linuxserver.davos.transfer.ftp.connection; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import io.linuxserver.davos.transfer.ftp.FTPFile; +import io.linuxserver.davos.transfer.ftp.exception.DownloadFailedException; +import io.linuxserver.davos.transfer.ftp.exception.FileListingException; +import io.linuxserver.davos.util.FileStreamFactory; +import io.linuxserver.davos.util.FileUtils; public class FTPConnection implements Connection { + private static final Logger LOGGER = LoggerFactory.getLogger(FTPConnection.class); + + private org.apache.commons.net.ftp.FTPClient client; + private FileStreamFactory fileStreamFactory = new FileStreamFactory(); + private FileUtils fileUtils = new FileUtils(); + public FTPConnection(org.apache.commons.net.ftp.FTPClient client) { - // TODO Auto-generated constructor stub + this.client = client; } - + @Override public String currentDirectory() { - // TODO Auto-generated method stub - return null; + + try { + return client.printWorkingDirectory(); + } catch (IOException e) { + throw new FileListingException("Unable to print the working directory", e); + } } @Override - public void download(FTPFile remoteFilePath, String localFilePath) { - // TODO Auto-generated method stub + public void download(FTPFile file, String localFilePath) { + String cleanRemotePath = FileUtils.ensureTrailingSlash(file.getPath()) + file.getName(); + String cleanLocalPath = FileUtils.ensureTrailingSlash(localFilePath); + + try { + + if (file.isDirectory()) + downloadDirectoryAndContents(file, cleanLocalPath, cleanRemotePath); + + else + doDownload(file, cleanRemotePath, cleanLocalPath); + + } catch (FileNotFoundException e) { + throw new DownloadFailedException( + String.format("Unable to write to local directory %s", cleanLocalPath + file.getName()), e); + } catch (IOException e) { + throw new DownloadFailedException(String.format("Unable to download file %s", cleanRemotePath), e); + } } @Override public List listFiles() { - // TODO Auto-generated method stub - return null; + return listFiles(currentDirectory()); } @Override public List listFiles(String remoteDirectory) { - // TODO Auto-generated method stub - return null; + + List files = new ArrayList(); + + try { + + String cleanRemoteDirectory = FileUtils.ensureTrailingSlash(remoteDirectory); + LOGGER.debug("Listing all files in {}", cleanRemoteDirectory); + org.apache.commons.net.ftp.FTPFile[] ftpFiles = client.listFiles(cleanRemoteDirectory); + + for (org.apache.commons.net.ftp.FTPFile file : ftpFiles) + files.add(toFtpFile(file, cleanRemoteDirectory)); + + } catch (IOException e) { + throw new FileListingException(String.format("Unable to list files in directory %s", remoteDirectory), e); + } + + return files.stream().filter(removeCurrentAndParentDirs()).collect(Collectors.toList()); } + private void doDownload(FTPFile file, String cleanRemotePath, String cleanLocalPath) + throws FileNotFoundException, IOException { + + LOGGER.info("Downloading {} to {}", cleanRemotePath, cleanLocalPath); + LOGGER.debug("Creating output stream for file {}", cleanLocalPath + file.getName()); + OutputStream outputStream = fileStreamFactory.createOutputStream(cleanLocalPath + file.getName()); + boolean hasDownloaded = client.retrieveFile(cleanRemotePath, outputStream); + outputStream.close(); + + if (!hasDownloaded) + throw new DownloadFailedException("Server returned failure while downloading."); + } + + private void downloadDirectoryAndContents(FTPFile file, String localDownloadFolder, String path) throws IOException { + + LOGGER.info("Item {} is a directory. Will now check sub-items", file.getName()); + List subItems = listFiles(path).stream().filter(removeCurrentAndParentDirs()).collect(Collectors.toList()); + + String fullLocalDownloadPath = FileUtils.ensureTrailingSlash(localDownloadFolder + file.getName()); + + LOGGER.debug("Creating new local directory {}", fullLocalDownloadPath); + fileUtils.createLocalDirectory(fullLocalDownloadPath); + + for (FTPFile subItem : subItems) { + + String subItemPath = FileUtils.ensureTrailingSlash(subItem.getPath()) + subItem.getName(); + + if (subItem.isDirectory()) { + + String subLocalFilePath = FileUtils.ensureTrailingSlash(fullLocalDownloadPath); + downloadDirectoryAndContents(subItem, subLocalFilePath, FileUtils.ensureTrailingSlash(subItemPath)); + } + + else + doDownload(subItem, subItemPath, fullLocalDownloadPath); + } + } + + private Predicate removeCurrentAndParentDirs() { + return file -> !file.getName().equals(".") && !file.getName().equals(".."); + } + + private FTPFile toFtpFile(org.apache.commons.net.ftp.FTPFile ftpFile, String filePath) throws IOException { + + String name = ftpFile.getName(); + long fileSize = ftpFile.getSize(); + long mTime = ftpFile.getTimestamp().getTime().getTime(); + boolean isDirectory = ftpFile.isDirectory(); + + return new FTPFile(name, fileSize, filePath, mTime, isDirectory); + } } diff --git a/src/main/java/io/linuxserver/davos/util/FileStreamFactory.java b/src/main/java/io/linuxserver/davos/util/FileStreamFactory.java new file mode 100644 index 0000000..4d1a140 --- /dev/null +++ b/src/main/java/io/linuxserver/davos/util/FileStreamFactory.java @@ -0,0 +1,17 @@ +package io.linuxserver.davos.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; + +public class FileStreamFactory { + + public FileInputStream createInputStream(String filePath) throws FileNotFoundException { + return new FileInputStream(new File(filePath)); + } + + public FileOutputStream createOutputStream(String filePath) throws FileNotFoundException { + return new FileOutputStream(new File(filePath)); + } +} diff --git a/src/test/java/io/linuxserver/davos/transfer/ftp/client/FTPSClientTest.java b/src/test/java/io/linuxserver/davos/transfer/ftp/client/FTPSClientTest.java new file mode 100644 index 0000000..6c75fae --- /dev/null +++ b/src/test/java/io/linuxserver/davos/transfer/ftp/client/FTPSClientTest.java @@ -0,0 +1,15 @@ +package io.linuxserver.davos.transfer.ftp.client; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; + +public class FTPSClientTest { + + private FTPClient client = new FTPSClient(); + + @Test + public void newFtpsClientShouldCreateFTPSClientInstance() { + assertThat(client.ftpClient).isInstanceOf(org.apache.commons.net.ftp.FTPSClient.class); + } +} diff --git a/src/test/java/io/linuxserver/davos/transfer/ftp/connection/FTPConnectionTest.java b/src/test/java/io/linuxserver/davos/transfer/ftp/connection/FTPConnectionTest.java new file mode 100644 index 0000000..ba6b302 --- /dev/null +++ b/src/test/java/io/linuxserver/davos/transfer/ftp/connection/FTPConnectionTest.java @@ -0,0 +1,347 @@ +package io.linuxserver.davos.transfer.ftp.connection; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.InOrder; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; + +import io.linuxserver.davos.transfer.ftp.FTPFile; +import io.linuxserver.davos.transfer.ftp.exception.DownloadFailedException; +import io.linuxserver.davos.transfer.ftp.exception.FileListingException; +import io.linuxserver.davos.util.FileStreamFactory; +import io.linuxserver.davos.util.FileUtils; + +public class FTPConnectionTest { + + private static final String LOCAL_DIRECTORY = "."; + private static final String DIRECTORY_PATH = "this/is/a/directory"; + + @InjectMocks + private FTPConnection ftpConnection; + + @Mock + private FileStreamFactory mockFileStreamFactory; + + @Mock + private FileUtils mockFileUtils; + + @Mock + private FileOutputStream mockFileOutputStream; + + private org.apache.commons.net.ftp.FTPClient mockFtpClient; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Before + public void setUp() throws IOException { + + mockFtpClient = mock(org.apache.commons.net.ftp.FTPClient.class); + + when(mockFtpClient.changeWorkingDirectory(anyString())).thenReturn(true); + when(mockFtpClient.printWorkingDirectory()).thenReturn(DIRECTORY_PATH); + when(mockFtpClient.retrieveFile(anyString(), any(OutputStream.class))).thenReturn(true); + + org.apache.commons.net.ftp.FTPFile[] files = createRemoteFTPFiles(); + + ftpConnection = new FTPConnection(mockFtpClient); + + initMocks(this); + + when(mockFtpClient.listFiles(anyString())).thenReturn(files); + when(mockFileStreamFactory.createOutputStream("./remote.file")).thenReturn(mockFileOutputStream); + } + + @Test + public void whenListingFilesThenFtpClientListFilesMethodShouldBeCalledForCurrentWorkingDirectory() throws IOException { + + ftpConnection.listFiles(); + + verify(mockFtpClient).listFiles("this/is/a/directory/"); + } + + @Test + public void ifWhenListingFilesFtpClientThrowsExceptionThenCatchAndRethrowFileListingExcepton() throws IOException { + + expectedException.expect(FileListingException.class); + expectedException.expectMessage(is(equalTo("Unable to list files in directory " + DIRECTORY_PATH))); + + when(mockFtpClient.listFiles("this/is/a/directory/")).thenThrow(new IOException()); + + ftpConnection.listFiles(); + } + + @Test + public void whenListingFilesThenFileArrayThatListFilesReturnsShouldBeConvertedToListOfFtpFilesAndReturned() + throws IOException { + + List returnedFiles = ftpConnection.listFiles(); + + assertThat(returnedFiles.get(0).getName()).isEqualTo("File 1"); + assertThat(returnedFiles.get(0).getSize()).isEqualTo(1000l); + assertThat(returnedFiles.get(0).getPath()).isEqualTo("this/is/a/directory/"); + assertThat(returnedFiles.get(0).isDirectory()).isFalse(); + + assertThat(returnedFiles.get(1).getName()).isEqualTo("File 2"); + assertThat(returnedFiles.get(1).getSize()).isEqualTo(2000l); + assertThat(returnedFiles.get(1).getPath()).isEqualTo("this/is/a/directory/"); + assertThat(returnedFiles.get(1).isDirectory()).isTrue(); + + assertThat(returnedFiles.get(2).getName()).isEqualTo("File 3"); + assertThat(returnedFiles.get(2).getSize()).isEqualTo(3000l); + assertThat(returnedFiles.get(2).getPath()).isEqualTo("this/is/a/directory/"); + assertThat(returnedFiles.get(2).isDirectory()).isFalse(); + } + + @Test + public void returnedFtpFilesShouldHaveCorrectModifiedDateTimesAgainstThem() { + + List files = ftpConnection.listFiles(); + + assertThat(files.get(0).getLastModified().toString("dd/MM/yyyy HH:mm:ss")).isEqualTo("19/03/2014 21:40:00"); + assertThat(files.get(1).getLastModified().toString("dd/MM/yyyy HH:mm:ss")).isEqualTo("19/03/2014 21:40:00"); + assertThat(files.get(2).getLastModified().toString("dd/MM/yyyy HH:mm:ss")).isEqualTo("19/03/2014 21:40:00"); + } + + @Test + public void whenListingFilesAndGivingRelativePathThenThatPathShouldBeUsedAlongsideCurrentWorkingDir() throws IOException { + + ftpConnection.listFiles("relativePath"); + + verify(mockFtpClient).listFiles("relativePath/"); + } + + @Test + public void downloadMethodShouldCreateLocalFileStreamFromCorrectPathBasedOnRemoteFileName() throws FileNotFoundException { + + FTPFile file = new FTPFile("remote.file", 0l, "path/to", 0, false); + ftpConnection.download(file, LOCAL_DIRECTORY); + + verify(mockFileStreamFactory).createOutputStream(LOCAL_DIRECTORY + "/remote.file"); + } + + @Test + public void downloadMethodShouldCallOnFtpClientRetrieveFilesMethodWithRemoteFilename() throws IOException { + + FTPFile file = new FTPFile("remote.file", 0l, "path/to", 0, false); + ftpConnection.download(file, LOCAL_DIRECTORY); + + verify(mockFtpClient).retrieveFile("path/to/remote.file", mockFileOutputStream); + } + + @Test + public void downloadMethodShouldThrowExceptionIfUnableToOpenStreamToLocalFile() throws IOException { + + expectedException.expect(DownloadFailedException.class); + expectedException.expectMessage(is(equalTo("Unable to write to local directory " + LOCAL_DIRECTORY + "/remote.file"))); + + when(mockFtpClient.retrieveFile("path/to/remote.file", mockFileOutputStream)).thenThrow(new FileNotFoundException()); + + FTPFile file = new FTPFile("remote.file", 0l, "path/to", 0, false); + ftpConnection.download(file, LOCAL_DIRECTORY); + } + + @Test + public void shouldDownloadFailForAnyReasonWhileInProgressThenCatchIOExceptionAndThrowNewDownloadFailedException() + throws IOException { + + expectedException.expect(DownloadFailedException.class); + expectedException.expectMessage(is(equalTo("Unable to download file path/to/remote.file"))); + + when(mockFtpClient.retrieveFile("path/to/remote.file", mockFileOutputStream)).thenThrow(new IOException()); + + FTPFile file = new FTPFile("remote.file", 0l, "path/to", 0, false); + ftpConnection.download(file, LOCAL_DIRECTORY); + } + + @Test + public void ifRetrieveFileMethodInClientReturnsFalseThenThrowDownloadFailedException() throws IOException { + + expectedException.expect(DownloadFailedException.class); + expectedException.expectMessage(is(equalTo("Server returned failure while downloading."))); + + when(mockFtpClient.retrieveFile("path/to/remote.file", mockFileOutputStream)).thenReturn(false); + + FTPFile file = new FTPFile("remote.file", 0l, "path/to", 0, false); + ftpConnection.download(file, LOCAL_DIRECTORY); + } + + @Test + public void printingWorkingDirectoryShouldCallOnUnderlyingClientMethodToGetCurrentDirectory() throws IOException { + + ftpConnection.currentDirectory(); + + verify(mockFtpClient).printWorkingDirectory(); + } + + @Test + public void printingWorkingDirectoryShouldReturnExactlyWhatTheUnderlyingClientReturns() { + assertThat(ftpConnection.currentDirectory()).isEqualTo(DIRECTORY_PATH); + } + + @Test + public void ifClientThrowsExceptionWhenTryingToGetWorkingDirectoryThenCatchExceptionAndRethrow() throws IOException { + + expectedException.expect(FileListingException.class); + expectedException.expectMessage(is(equalTo("Unable to print the working directory"))); + + when(mockFtpClient.printWorkingDirectory()).thenThrow(new IOException()); + + ftpConnection.currentDirectory(); + } + + @Test + public void downloadShouldRecursivelyCheckFileIfFolderThenLsThatAndGetOnlyFiles() throws IOException { + + initRecursiveListings(); + + FileOutputStream stream1 = mock(FileOutputStream.class); + FileOutputStream stream2 = mock(FileOutputStream.class); + FileOutputStream stream3 = mock(FileOutputStream.class); + FileOutputStream stream4 = mock(FileOutputStream.class); + FileOutputStream stream5 = mock(FileOutputStream.class); + FileOutputStream stream6 = mock(FileOutputStream.class); + + when(mockFileStreamFactory.createOutputStream("some/directory/folder/file1.txt")).thenReturn(stream1); + when(mockFileStreamFactory.createOutputStream("some/directory/folder/file2.txt")).thenReturn(stream2); + when(mockFileStreamFactory.createOutputStream("some/directory/folder/directory1/file3.txt")).thenReturn(stream3); + when(mockFileStreamFactory.createOutputStream("some/directory/folder/directory1/directory2/file5.txt")) + .thenReturn(stream4); + when(mockFileStreamFactory.createOutputStream("some/directory/folder/directory1/directory2/file6.txt")) + .thenReturn(stream5); + when(mockFileStreamFactory.createOutputStream("some/directory/folder/directory1/file4.txt")).thenReturn(stream6); + + FTPFile directory = new FTPFile("folder", 0, "path/to", 0, true); + ftpConnection.download(directory, "some/directory"); + + verify(mockFileUtils).createLocalDirectory("some/directory/folder/"); + verify(mockFtpClient).listFiles("path/to/folder/"); + + verify(mockFileUtils).createLocalDirectory("some/directory/folder/directory1/"); + verify(mockFtpClient).listFiles("path/to/folder/directory1/"); + + verify(mockFileUtils).createLocalDirectory("some/directory/folder/directory1/directory2/"); + verify(mockFtpClient).listFiles("path/to/folder/directory1/directory2/"); + + InOrder inOrder = Mockito.inOrder(mockFtpClient, stream1, stream2, stream3, stream4, stream5, stream6); + + inOrder.verify(mockFtpClient).retrieveFile("path/to/folder/file1.txt", stream1); + inOrder.verify(stream1).close(); + inOrder.verify(mockFtpClient).retrieveFile("path/to/folder/file2.txt", stream2); + inOrder.verify(stream2).close(); + inOrder.verify(mockFtpClient).retrieveFile("path/to/folder/directory1/file3.txt", stream3); + inOrder.verify(stream3).close(); + inOrder.verify(mockFtpClient).retrieveFile("path/to/folder/directory1/directory2/file5.txt", stream4); + inOrder.verify(stream4).close(); + inOrder.verify(mockFtpClient).retrieveFile("path/to/folder/directory1/directory2/file6.txt", stream5); + inOrder.verify(stream5).close(); + inOrder.verify(mockFtpClient).retrieveFile("path/to/folder/directory1/file4.txt", stream6); + inOrder.verify(stream6).close(); + } + + private void initRecursiveListings() throws IOException { + + org.apache.commons.net.ftp.FTPFile[] entries = new org.apache.commons.net.ftp.FTPFile[5]; + + entries[0] = (createSingleEntry(".", 123l, 1394525265, true)); + entries[1] = (createSingleEntry("..", 123l, 1394525265, true)); + entries[2] = (createSingleEntry("file1.txt", 123l, 1394525265, false)); + entries[3] = (createSingleEntry("file2.txt", 456l, 1394652161, false)); + entries[4] = (createSingleEntry("directory1", 789l, 1391879364, true)); + + when(mockFtpClient.listFiles("path/to/folder/")).thenReturn(entries); + + org.apache.commons.net.ftp.FTPFile[] subEntries = new org.apache.commons.net.ftp.FTPFile[5]; + + subEntries[0] = (createSingleEntry(".", 123l, 1394525265, true)); + subEntries[1] = (createSingleEntry("..", 123l, 1394525265, true)); + subEntries[2] = (createSingleEntry("file3.txt", 789l, 1394525265, false)); + subEntries[3] = (createSingleEntry("directory2", 789l, 1394525265, true)); + subEntries[4] = (createSingleEntry("file4.txt", 789l, 1394525265, false)); + + when(mockFtpClient.listFiles("path/to/folder/directory1/")).thenReturn(subEntries); + + org.apache.commons.net.ftp.FTPFile[] subSubEntries = new org.apache.commons.net.ftp.FTPFile[4]; + + subSubEntries[0] = (createSingleEntry(".", 123l, 1394525265, true)); + subSubEntries[1] = (createSingleEntry("..", 123l, 1394525265, true)); + subSubEntries[2] = (createSingleEntry("file5.txt", 789l, 1394525265, false)); + subSubEntries[3] = (createSingleEntry("file6.txt", 789l, 1394525265, false)); + + when(mockFtpClient.listFiles("path/to/folder/directory1/directory2/")).thenReturn(subSubEntries); + } + + private org.apache.commons.net.ftp.FTPFile createSingleEntry(String fileName, long size, int mTime, boolean directory) { + + org.apache.commons.net.ftp.FTPFile file = mock(org.apache.commons.net.ftp.FTPFile.class); + + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new Date(mTime)); + + when(file.getName()).thenReturn(fileName); + when(file.getTimestamp()).thenReturn(calendar); + when(file.getSize()).thenReturn(size); + when(file.isDirectory()).thenReturn(directory); + + return file; + } + + private org.apache.commons.net.ftp.FTPFile[] createRemoteFTPFiles() { + + Calendar calendar = Calendar.getInstance(); + calendar.set(2014, 2, 19, 21, 40, 00); + + org.apache.commons.net.ftp.FTPFile[] files = new org.apache.commons.net.ftp.FTPFile[5]; + + org.apache.commons.net.ftp.FTPFile currentDir = mock(org.apache.commons.net.ftp.FTPFile.class); + when(currentDir.getName()).thenReturn("."); + when(currentDir.getTimestamp()).thenReturn(calendar); + + org.apache.commons.net.ftp.FTPFile parentDir = mock(org.apache.commons.net.ftp.FTPFile.class); + when(parentDir.getName()).thenReturn(".."); + when(parentDir.getTimestamp()).thenReturn(calendar); + + files[0] = currentDir; + files[1] = parentDir; + + for (int i = 2; i < 5; i++) { + + org.apache.commons.net.ftp.FTPFile file = mock(org.apache.commons.net.ftp.FTPFile.class); + + when(file.getName()).thenReturn("File " + (i - 1)); + when(file.getSize()).thenReturn((long) (i - 1) * 1000); + when(file.getTimestamp()).thenReturn(calendar); + when(file.isDirectory()).thenReturn(setTrueIfNumberIsEven(i)); + + files[i] = file; + } + + return files; + } + + private boolean setTrueIfNumberIsEven(int i) { + return (i + 1) % 2 == 0 ? true : false; + } +} \ No newline at end of file