diff --git a/src/main/java/ru/yandex/qatools/embed/postgresql/PostgresExecutable.java b/src/main/java/ru/yandex/qatools/embed/postgresql/PostgresExecutable.java index 7f9382f..c935b98 100644 --- a/src/main/java/ru/yandex/qatools/embed/postgresql/PostgresExecutable.java +++ b/src/main/java/ru/yandex/qatools/embed/postgresql/PostgresExecutable.java @@ -3,25 +3,77 @@ import de.flapdoodle.embed.process.config.IRuntimeConfig; import de.flapdoodle.embed.process.distribution.Distribution; import de.flapdoodle.embed.process.extract.IExtractedFileSet; +import de.flapdoodle.embed.process.runtime.ProcessControl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; +import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.stream.Stream; /** * postgres executable */ public class PostgresExecutable extends AbstractPGExecutable { - final IRuntimeConfig runtimeConfig; - public PostgresExecutable(Distribution distribution, PostgresConfig config, IRuntimeConfig runtimeConfig, IExtractedFileSet exe) { super(distribution, config, runtimeConfig, exe); - this.runtimeConfig = runtimeConfig; } @Override protected PostgresProcess start(Distribution distribution, PostgresConfig config, IRuntimeConfig runtime) throws IOException { + addShutdownHook(runtime, distribution); return new PostgresProcess(distribution, config, runtime, this); } -} \ No newline at end of file + + private void addShutdownHook(IRuntimeConfig runtimeConfig, Distribution distribution) throws IOException { + ProcessControl.addShutdownHook( + new CleanerRunner(runtimeConfig.getArtifactStore().extractFileSet(distribution).baseDir()) + ); + } + + static class CleanerRunner implements Runnable { + + private File fileOrDirectory; + + CleanerRunner(File fileOrDirectory) { + this.fileOrDirectory = fileOrDirectory; + } + + // TODO インスタンス生成するのはここで良いのか?もっと違うやり方があるのでは? + DirectoryCleaner getCleaner() { + return new DirectoryCleaner(); + } + + @Override + public void run() { + getCleaner().clean(this.fileOrDirectory); + } + } + + static class DirectoryCleaner { + private static Logger logger = LoggerFactory.getLogger(DirectoryCleaner.class); + + void clean(File cleanupTarget) { + synchronized (DirectoryCleaner.class) { + if (!cleanupTarget.exists()) { + return; + } + + try (Stream stream = Files.walk(cleanupTarget.toPath())) { + stream.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .filter(File::exists) + .forEach(File::delete); + } catch (IOException e) { + logger.warn("Could not delete {}.", cleanupTarget.getAbsolutePath(), e); + } + } + } + } +} diff --git a/src/test/java/ru/yandex/qatools/embed/postgresql/TestDeleteTemporaryDirectory.java b/src/test/java/ru/yandex/qatools/embed/postgresql/TestDeleteTemporaryDirectory.java new file mode 100644 index 0000000..38b2ed6 --- /dev/null +++ b/src/test/java/ru/yandex/qatools/embed/postgresql/TestDeleteTemporaryDirectory.java @@ -0,0 +1,97 @@ +package ru.yandex.qatools.embed.postgresql; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.UUID; +import java.util.stream.Stream; + +import static org.junit.Assert.assertFalse; + +@RunWith(MockitoJUnitRunner.class) +public class TestDeleteTemporaryDirectory { + + private static final Logger logger = LoggerFactory.getLogger(TestDeleteTemporaryDirectory.class); + private Path testDirectory; + + @Before + public void setup() { + testDirectory = Paths.get(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()); + } + + @After + public void cleanup() { + if (!testDirectory.toFile().exists()) { + return; + } + + try (Stream stream = Files.walk(testDirectory)) { + stream.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .filter(File::exists) + .forEach(File::delete); + } catch (IOException e) { + logger.warn("Could not delete {}.", testDirectory.toFile().getAbsolutePath(), e); + } + } + + @Test + public void callCleanMethodWhenCleanerRunnerIsRun() { + // Mock DirectoryCleaner + PostgresExecutable.DirectoryCleaner cleaner = Mockito.spy(PostgresExecutable.DirectoryCleaner.class); + Mockito.doNothing().when(cleaner).clean(Mockito.any(File.class)); + + // Run CleanerRunner + PostgresExecutable.CleanerRunner runner = + Mockito.spy(new PostgresExecutable.CleanerRunner(testDirectory.toFile())); + Mockito.doReturn(cleaner).when(runner).getCleaner(); + runner.run(); + + // Clean method was called by CleanerRunner + Mockito.verify(cleaner, Mockito.times(1)).clean(testDirectory.toFile()); + } + + @Test + public void directoryCleanerDeleteFilesInDirectoryAndItself() throws IOException { + createTestDirectories(testDirectory); + + // Run clean method + new PostgresExecutable.DirectoryCleaner().clean(testDirectory.toFile()); + + // Removed test directory + assertFalse("Directory should be removed. " + testDirectory, testDirectory.toFile().exists()); + } + + private void createTestDirectories(Path root) throws IOException { + // foo/ + // ├── file1 + // ├── file2 + // ├── bar/ + // │   ├── fileA + // │   └── fileB + // └── baz/ + Path foo = Paths.get(root.toFile().getAbsolutePath(), "foo"); + Path bar = Paths.get(foo.toFile().getAbsolutePath(), "bar"); + Path baz = Paths.get(foo.toFile().getAbsolutePath(), "baz"); + Files.createDirectories(foo); + Files.createDirectories(bar); + Files.createDirectories(baz); + + Files.createFile(Paths.get(foo.toFile().getAbsolutePath(), "file1")); + Files.createFile(Paths.get(foo.toFile().getAbsolutePath(), "file2")); + Files.createFile(Paths.get(bar.toFile().getAbsolutePath(), "fileA")); + Files.createFile(Paths.get(bar.toFile().getAbsolutePath(), "fileB")); + } +}