org.syncany.tests.unit.util.TestFileUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.syncany.tests.unit.util.TestFileUtil.java

Source

/*
 * Syncany, www.syncany.org
 * Copyright (C) 2011-2015 Philipp C. Heckel <philipp.heckel@gmail.com> 
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.syncany.tests.unit.util;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.syncany.util.FileUtil;

/**
 * This class provides file I/O helper methods for writing tests
 * 
 * @author Philipp Heckel <philipp.heckel@gmail.com>
 * @author Nikolai Hellwig
 * @author Andreas Fenske
 */
public class TestFileUtil {
    private static String IGNORE_DIR_APPLICATION = ".syncany"; // same as Config.DIR_APPLICATION   
    private static Random randomGen = new Random();
    private static Random nonRandomGen = new Random(123456789L); // fixed seed!

    public static File copyFile(File fromFile, File toFile) throws IOException {
        InputStream in = new FileInputStream(fromFile);
        OutputStream out = new FileOutputStream(toFile);

        byte[] buffer = new byte[1024];

        int length;
        // copy the file content in bytes
        while ((length = in.read(buffer)) > 0) {
            out.write(buffer, 0, length);
        }

        in.close();
        out.close();

        toFile.setLastModified(fromFile.lastModified()); // Windows changes last modified when copying file

        return toFile;
    }

    public static File createTempDirectoryInSystemTemp() throws Exception {
        return createTempDirectoryInSystemTemp("syncanytest");
    }

    public static File getAppTempDir() {
        String tempDirStr = System.getProperty("org.syncany.test.tmpdir");

        if (tempDirStr == null) {
            tempDirStr = System.getProperty("java.io.tmpdir");
        }

        return new File(tempDirStr, "syncanytest");
    }

    public static File createTempDirectoryInSystemTemp(String prefix) throws Exception {
        File tempDirectoryInSystemTemp = new File(getAppTempDir() + "/" + prefix);

        int i = 1;
        while (tempDirectoryInSystemTemp.exists()) {
            tempDirectoryInSystemTemp = new File(getAppTempDir() + "/" + prefix + "-" + i);
            i++;
        }

        if (!tempDirectoryInSystemTemp.mkdirs()) {
            throw new Exception("Cannot create temp. directory " + tempDirectoryInSystemTemp);
        }

        return tempDirectoryInSystemTemp;
    }

    public static boolean deleteDirectory(File path) {
        if (path != null && path.exists() && path.isDirectory()) {
            File[] files = path.listFiles();
            for (int i = 0; i < files.length; i++) {
                if (files[i].isDirectory()) {
                    deleteDirectory(files[i]);
                } else {
                    files[i].delete();
                }
            }
        } else
            return false;
        return (path.delete());
    }

    public static boolean deleteFile(File file) {
        if (file != null && file.exists() && file.isFile()) {
            return file.delete();
        } else
            return false;
    }

    public static void changeRandomPartOfBinaryFile(File file) throws IOException {
        if (file != null && !file.exists()) {
            throw new IOException("File does not exist: " + file);
        }

        if (file.isDirectory()) {
            throw new IOException("Cannot change directory: " + file);
        }

        // Prepare: random bytes at random position
        Random randomEngine = new Random();

        int fileSize = (int) file.length();
        int maxChangeBytesLen = 20;
        int maxChangeBytesStartPos = (fileSize - maxChangeBytesLen - 1 >= 0) ? fileSize - maxChangeBytesLen - 1 : 0;

        int changeBytesStartPos = (maxChangeBytesStartPos > 0) ? randomEngine.nextInt(maxChangeBytesStartPos) : 0;
        int changeBytesLen = (fileSize - changeBytesStartPos < maxChangeBytesLen)
                ? fileSize - changeBytesStartPos - 1
                : maxChangeBytesLen;

        byte[] changeBytes = new byte[changeBytesLen];
        randomEngine.nextBytes(changeBytes);

        // Write to file
        RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");

        randomAccessFile.seek(changeBytesStartPos);
        randomAccessFile.write(changeBytes);

        randomAccessFile.close();
    }

    public static File getRandomFilenameInDirectory(File rootFolder) {
        String fileName = "rndFile-" + System.currentTimeMillis() + "-" + Math.abs(randomGen.nextInt()) + ".dat";
        File newRandomFile = new File(rootFolder, fileName);

        return newRandomFile;
    }

    public static List<File> createRandomFileTreeInDirectory(File rootFolder, int maxFiles) throws IOException {
        List<File> randomFiles = new ArrayList<File>();
        List<File> randomDirs = new ArrayList<File>();
        File currentDir = rootFolder;

        for (int i = 0; i < maxFiles; i++) {
            if (!randomDirs.isEmpty()) {
                currentDir = randomDirs.get((int) Math.random() * randomDirs.size());
            }

            if (Math.random() > 0.3) {
                File newFile = new File(currentDir + "/file" + i);
                int newFileSize = (int) Math.round(1000.0 + Math.random() * 500000.0);

                createRandomFile(newFile, newFileSize);
                randomFiles.add(newFile);
            } else {
                currentDir = new File(currentDir + "/folder" + i);
                currentDir.mkdir();

                randomDirs.add(currentDir);
                randomFiles.add(currentDir);
            }
        }

        // Now copy some files (1:1 copy), and slightly change some of them (1:0.9)
        for (int i = maxFiles; i < maxFiles + maxFiles / 4; i++) {
            File srcFile = randomFiles.get((int) (Math.random() * (double) randomFiles.size()));
            File destDir = randomDirs.get((int) (Math.random() * (double) randomDirs.size()));

            if (srcFile.isDirectory()) {
                continue;
            }

            // Alter some of the copies (change some bytes)
            if (Math.random() > 0.5) {
                File destFile = new File(destDir + "/file" + i + "-almost-the-same-as-" + srcFile.getName());
                FileUtils.copyFile(srcFile, destFile);

                changeRandomPartOfBinaryFile(destFile);
                randomFiles.add(destFile);
            }

            // Or simply copy them
            else {
                File destFile = new File(destDir + "/file" + i + "-copy-of-" + srcFile.getName());
                FileUtils.copyFile(srcFile, destFile);

                randomFiles.add(destFile);
            }
        }

        return randomFiles;
    }

    public static List<File> createRandomFilesInDirectory(File rootFolder, long sizeInBytes, int numOfFiles)
            throws IOException {
        List<File> newRandomFiles = new ArrayList<File>();

        for (int i = 0; i < numOfFiles; i++) {
            newRandomFiles.add(createRandomFileInDirectory(rootFolder, sizeInBytes));
        }

        return newRandomFiles;
    }

    public static File createRandomFileInDirectory(File rootFolder, long sizeInBytes) throws IOException {
        File newRandomFile = getRandomFilenameInDirectory(rootFolder);
        createRandomFile(newRandomFile, sizeInBytes);

        return newRandomFile;
    }

    public static void createNonRandomFile(File fileToCreate, long sizeInBytes) throws IOException {
        createFile(fileToCreate, sizeInBytes, nonRandomGen);
    }

    public static void createFileWithContent(File fileToCreate, String content) throws IOException {
        if (fileToCreate != null && fileToCreate.exists()) {
            throw new IOException("File already exists");
        }
        PrintWriter writer = new PrintWriter(fileToCreate);
        writer.print(content);
        writer.close();
    }

    public static void createRandomFile(File fileToCreate, long sizeInBytes) throws IOException {
        createFile(fileToCreate, sizeInBytes, randomGen);
    }

    private static void createFile(File fileToCreate, long sizeInBytes, Random randomGen) throws IOException {
        if (fileToCreate != null && fileToCreate.exists()) {
            throw new IOException("File already exists");
        }

        FileOutputStream fos = new FileOutputStream(fileToCreate);
        int bufSize = 4096;
        long cycles = sizeInBytes / (long) bufSize;

        for (int i = 0; i < cycles; i++) {
            byte[] randomByteArray = createArray(bufSize, randomGen);
            fos.write(randomByteArray);
        }

        // create last one
        // modulo cannot exceed integer range, so cast should be ok
        byte[] arr = createArray((int) (sizeInBytes % bufSize), randomGen);
        fos.write(arr);

        fos.close();
    }

    public static void writeByteArrayToFile(byte[] inputByteArray, File fileToCreate) throws IOException {
        FileOutputStream fos = new FileOutputStream(fileToCreate);
        fos.write(inputByteArray);
        fos.close();
    }

    public static byte[] createArray(int size, Random randomGen) {
        byte[] ret = new byte[size];
        randomGen.nextBytes(ret);
        return ret;
    }

    public static byte[] createRandomArray(int size) {
        return createArray(size, randomGen);
    }

    public static byte[] createChecksum(File file) throws Exception {
        return FileUtil.createChecksum(file, "SHA1");
    }

    public static Map<String, File> getLocalFiles(File root) throws FileNotFoundException {
        return getLocalFiles(root, null);
    }

    public static Map<String, File> getLocalFilesExcludeLockedAndNoRead(File root) throws FileNotFoundException {
        return getLocalFiles(root, new FileFilter() {
            @Override
            public boolean accept(File file) {
                return !FileUtil.isFileLocked(file) && canRead(file);
            }
        });
    }

    public static Map<String, File> getLocalFiles(File root, FileFilter filter) throws FileNotFoundException {
        List<File> fileList = getRecursiveFileList(root, true, false);
        Map<String, File> fileMap = new HashMap<String, File>();

        for (File file : fileList) {
            if (filter != null && !filter.accept(file)) {
                continue;
            }

            String relativePath = FileUtil.getRelativePath(root, file);

            if (relativePath.startsWith(IGNORE_DIR_APPLICATION)) {
                continue;
            }

            fileMap.put(relativePath, file);
        }

        return fileMap;
    }

    /**
     * Replaces the {@link File#canRead() canRead()} method in the {@link File} class by taking
     * symlinks into account. Returns <tt>true</tt> if a symlink exists even if its target file
     * does not exist and can hence not be read.
     * 
     * @param file A file
     * @return Returns <tt>true</tt> if the file can be read (or the symlink exists), <tt>false</tt> otherwise
     */
    public static boolean canRead(File file) {
        if (FileUtil.isSymlink(file)) {
            return FileUtil.exists(file);
        } else {
            return file.canRead();
        }
    }

    public static void writeToFile(byte[] bytes, File file) throws IOException {
        FileOutputStream outputStream = new FileOutputStream(file);

        IOUtils.copy(new ByteArrayInputStream(bytes), outputStream);
        outputStream.close();
    }

    public static String getBasename(String filename) {
        int dot = filename.lastIndexOf(".");

        if (dot == -1) {
            return filename;
        }

        return filename.substring(0, dot);
    }

    public static List<File> getRecursiveFileList(File root) throws FileNotFoundException {
        return getRecursiveFileList(root, false, false);
    }

    public static List<File> getRecursiveFileList(File root, boolean includeDirectories,
            boolean followSymlinkDirectories) throws FileNotFoundException {
        if (!root.isDirectory() || !root.canRead() || !root.exists()) {
            throw new FileNotFoundException("Invalid directory " + root);
        }

        List<File> result = getRecursiveFileListNoSort(root, includeDirectories, followSymlinkDirectories);
        Collections.sort(result);

        return result;
    }

    private static List<File> getRecursiveFileListNoSort(File root, boolean includeDirectories,
            boolean followSymlinkDirectories) {
        List<File> result = new ArrayList<File>();
        List<File> filesDirs = Arrays.asList(root.listFiles());

        for (File file : filesDirs) {
            boolean isDirectory = file.isDirectory();
            boolean isSymlinkDirectory = isDirectory && FileUtil.isSymlink(file);
            boolean includeFile = !isDirectory || includeDirectories;
            boolean followDirectory = (isSymlinkDirectory && followSymlinkDirectories)
                    || (isDirectory && !isSymlinkDirectory);

            if (includeFile) {
                result.add(file);
            }

            if (followDirectory) {
                List<File> deeperList = getRecursiveFileListNoSort(file, includeDirectories,
                        followSymlinkDirectories);
                result.addAll(deeperList);
            }
        }

        return result;
    }
}