Создайте файл, который не может быть удален с помощью file.delete () - PullRequest
0 голосов
/ 12 июня 2018

Примечание: я знаю, что есть несколько вопросов, похожих на этот, однако я не могу найти ни одного, который объясняет, как разрешить ситуацию, которую я пытаюсь разрешить.Я задам этот вопрос на конкретном примере, для которого мне нужно решение.

Рассмотрим код:

private final void writeToFile(final File parent, final String filename, final Charset charset, final String content) throws IOException {
    final File file = new File(parent, filename);

    if (file.exists()) {
        LOG.warn("File {} already exists, file will be replaced.", file.getCanonicalPath());
        if (!file.delete()) {
            logAndThrow(String.format("Cannot delete file '%s'.", file.getCanonicalPath()), null);
        }
    }

    try (final FileOutputStream fos = new FileOutputStream(file);
            OutputStreamWriter writer = new OutputStreamWriter(fos, charset)) {
        writer.write(content);
    }
}

Я пытаюсь написать модульный тест, чтобы вызвать выбрасываемое IOExceptionкогда код не может удалить файл.Модульный тест, который я попробовал, выглядит следующим образом:

@Test public void testFileNotDeletable() throws IOException {
  final File file = new File(folder.getRoot(), formattedFile.getMetaData().getFormattedCaptureFileName());
  file.createNewFile();
  try {
    file.setReadOnly();

    exception.expect(IOException.class);
    exception.expectMessage(String.format("Cannot delete file '%s'.", file.getCanonicalPath()));

    writer.write(formattedFile);
  } finally {
    file.setWritable(true);
  }
}

Я также пытался заблокировать файл:

@Test public void testFileNotDeletable() throws IOException {
    final File file = new File(folder.getRoot(), formattedFile.getMetaData().getFormattedCaptureFileName());
    file.createNewFile();
    try (FileInputStream fis = new FileInputStream(file)) {
        final FileLock lock = fis.getChannel().tryLock(0L, Long.MAX_VALUE, true);
        try {
            exception.expect(IOException.class);
            exception.expectMessage(String.format("Cannot delete file '%s'.", file.getCanonicalPath()));

            writer.write(formattedFile);
        } finally {
            lock.release();
        }
    }
}

Независимо от того, что я пытаюсь, file.delete () успешно удаляетфайл и тест не пройден, так как ожидаемое исключение IOException не было выдано.

Любая помощь с благодарностью.

Примечание: добавлен для пояснения некоторый дополнительный код, который показывает, что объект File полностьюотдельно в окружающей среде.Файл formattedFile, передаваемый методу записи, не является File или подклассом File, это один из наших внутренних классов.Файл в тесте JUnit использует TemporaryFolder для корня, а formattedFile имеет элемент MetaData, который определяет имя файла.В моем тесте JUnit я пытаюсь создать пустой файл, который не может быть удален, в месте, где мой фактический код попытается записать файл.Мне нужно, чтобы file.delete () возвратил false, чтобы я мог проверить, что генерируется исключение.Поэтому я не могу издеваться над объектом File.

Ответы [ 5 ]

0 голосов
/ 14 июня 2018

В Linux вы можете с помощью команды chattr установить «неизменяемый» файл, который не может быть удален даже пользователем root.Кто-то еще сказал «установить права доступа к файлу», что верно, но не дал конкретной детали.

Приветствия D

0 голосов
/ 12 июня 2018

Есть два решения для вашего вопроса, я рекомендую первое.

  • Решение 1 Вы не тестируете операции / класс операций ввода-вывода java-файлаздесь вы тестируете функциональное поведение вашего кода в ответ на файловую операцию.Итак, в идеале в вашем JUnit вы должны насмехаться над объектом File и его соответствующими вызовами и сосредоточиться только на тестировании вашего кода.

  • Решение 2 Если вывсе еще хотите проверить полную интеграцию с вводом-выводом файла Java, откройте файл в режиме записи, прежде чем пытаться удалить, и он позаботится о вашем тестовом примере.

ПРИМЕЧАНИЕ: Код, протестированный в CENTOS, WINDOWS, UBUNTU, MAC OS-X

Тематический класс:

    public class FileSolution {
        public void fileHandler(File file) throws IOException, Exception {
            if (file.exists()) {
                LOG.warn("File {} already exists, file will be replaced.", 
                        file.getCanonicalPath());
                if (!file.delete()) {
                    logAndThrow(String.format("Cannot delete file '%s'.", 
                            file.getCanonicalPath()),
                            new IOException(String.format("Cannot delete file '%s'.", 
                                    file.getCanonicalPath())));
                }
            }
        }
    }

Тема теста Uner:

import static org.mockito.BDDMockito.*;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

public class FileSolutionTest {

    @Rule
    public final ExpectedException exception = ExpectedException.none();

    /**
     * Solution 1
     * 
     * @throws Exception
     */
    @Test
    public void testFileNotDeletableWithMock() throws Exception {
        final File file = mock(File.class);
        file.createNewFile();
        // mock file & IO operations
        given(file.exists()).willReturn(true);
        given(file.delete()).willReturn(false);
        given(file.getCanonicalPath()).willReturn("test.txt");

        exception.expect(IOException.class);
        exception.expectMessage(String.format("Cannot delete file '%s'.", file.getCanonicalPath()));

        new FileSolution().fileHandler(file);
    }

    /**
     * Solution 2
     * 
     * @throws Exception
     */
    @Test
    public void testFileNotDeletable() throws Exception {
        File file = null;
        FileWriter fileWriter = null;
        try{
            file = new File("test.txt");
            file.createNewFile();
            file.deleteOnExit();
            exception.expect(IOException.class);
            exception.expectMessage(String.format("Cannot delete file '%s'.", file.getCanonicalPath()));
            // open file with another process for writing
            fileWriter = new FileWriter(file, true);
            new FileSolution().fileHandler(file);
        } finally{
            if(fileWriter != null){
                fileWriter.flush();
                fileWriter.close();
            }
        }
    }
}
0 голосов
/ 12 июня 2018

Я полностью согласен с Turing85 относительно использования mockito.

Давайте представим, что у вас есть оригинальный класс с методом, похожим на тот, который вы хотите проверить:

public class FileDel {

    public void logOnIOException(File file) throws IOException {
        if (file.exists()) {
            LOG.warn("File {} already exists, file will be replaced.", file.getCanonicalPath());
            if (!file.delete()) {
                logAndThrow(String.format("Cannot delete file '%s'.", file.getCanonicalPath()), null);
            }
        }
    }

    public void logAndThrow(String msg, String s) {
        //Do nothing
    }

    private static class LOG {
        public static void warn(String msg, String path) {
        }
    }
}

Затем вы можете вызватьвнутреннее исключение таким образом:

@RunWith(MockitoJUnitRunner.class)
public class FileDelTest {
    @Test(expected = IOException.class)
    public void testFileNotDeletable() throws IOException {
        File file = mock(File.class);
        when(file.exists()).thenReturn(true);
        when(file.delete()).thenAnswer(new Answer<Boolean>() {
            @Override
            public Boolean answer(InvocationOnMock iom) throws Throwable {
                throw new IOException();
            }
        });

        FileDel f = new FileDel();

        try {
            f.methodToTest(file);
        } finally {
        }
    }
}
0 голосов
/ 12 июня 2018

Как насчет открыть InputStream для этого файла и не закрывать его.Пока дескриптор файла не будет закрыт, файл не будет удален.

0 голосов
/ 12 июня 2018

Чтобы предотвратить удаление файла, вы должны отказать в разрешении безопасности в Windows.Из пользовательского интерфейса нам нужно сделать что-то вроде

  1. Щелкните правой кнопкой мыши файл или документ на вашем ПК => Выбрать свойства;
  2. ВБезопасность, вкладка Изменить для изменения разрешения => Выберите Добавить и введите Все;
  3. Нажмите OK и выберите группу, чтобы изменить разрешение на Полный доступ на Запретить;
  4. Нажмите Да для подтверждения.

Единственный известный мне способ изменения прав доступа к файлам в Java:

file.setExecutable(true|false);
file.setReadable(true|false);
file.setWritable(true|false);

и

File file = new File("test.txt");

if(file.exists())
{
    //Setting file permissions for owner, group and others using PosixFilePermission

    HashSet<PosixFilePermission> set = new HashSet<PosixFilePermission>();

    //Adding owner's file permissions

    set.add(PosixFilePermission.OWNER_EXECUTE);
    set.add(PosixFilePermission.OWNER_READ);
    set.add(PosixFilePermission.OWNER_WRITE);

    //Adding group's file permissions

    set.add(PosixFilePermission.GROUP_EXECUTE);
    set.add(PosixFilePermission.GROUP_READ);
    set.add(PosixFilePermission.GROUP_WRITE);

    //Adding other's file permissions

    set.add(PosixFilePermission.OTHERS_EXECUTE);
    set.add(PosixFilePermission.OTHERS_READ);
    set.add(PosixFilePermission.OTHERS_WRITE);

    Files.setPosixFilePermissions(Paths.get("test.txt"), set);
}
else
{
    System.out.println("Sorry...File doesn't exist.");
}

Итак, предотвращение удаления файла, я предполагаю, будет связано с разрешениями на запись в файл.Попробуйте отключить права на запись и, возможно, права на выполнение, прежде чем пытаться удалить файл.

Если это не сработает, то я не верю, что это можно сделать с помощью языка Java, поскольку на данный момент это единственные методы, доступные для изменения прав доступа к файлам.Я могу ошибаться, но я не смог найти ничего другого.

ОБНОВЛЕНИЕ

Для Linux попробуйте следующее:

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class ExecuteShellComand {

    public static void main(String[] args) {

        ExecuteShellComand obj = new ExecuteShellComand();

        String command = "sudo chattr +i /backups/passwd";
        // OR try

        //String command = "sudo chattr +i -V /backups/passwd";

        String output = obj.executeCommand(command);

        System.out.println(output);

    }

    private String executeCommand(String command) {

        StringBuffer output = new StringBuffer();

        Process p;
        try {
            p = Runtime.getRuntime().exec(command);
            p.waitFor();
            BufferedReader reader = 
                            new BufferedReader(new InputStreamReader(p.getInputStream()));

                        String line = "";           
            while ((line = reader.readLine())!= null) {
                output.append(line + "\n");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return output.toString();

    }
}

Приведенный выше файл / backups / passwd неизменен (или не может быть удален).Это означает, что файл не может быть изменен каким-либо образом: его нельзя удалить или переименовать.Вы даже не можете создать ссылку на него, и никакие данные также не могут быть записаны в файл.Это единственное, о чем я могу думать.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...