From 05498e1aca50fa08634246517a585e7737c4ac5a Mon Sep 17 00:00:00 2001 From: fu-taku Date: Thu, 23 Aug 2018 13:34:39 +0900 Subject: [PATCH 1/5] Add DirectoryCleaner postgresql-embedded leave directory in temporary space. This is because of sequence sometimes does not called when process is killed. So, I want to add a cleaner to shutdown hook. --- .../embed/postgresql/PostgresExecutable.java | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) 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..d5af0bd 100644 --- a/src/main/java/ru/yandex/qatools/embed/postgresql/PostgresExecutable.java +++ b/src/main/java/ru/yandex/qatools/embed/postgresql/PostgresExecutable.java @@ -3,9 +3,19 @@ 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.io.directories.IDirectory; +import de.flapdoodle.embed.process.runtime.ProcessControl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; +import ru.yandex.qatools.embed.postgresql.ext.SubdirTempDir; +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 @@ -17,6 +27,7 @@ public PostgresExecutable(Distribution distribution, PostgresConfig config, IRuntimeConfig runtimeConfig, IExtractedFileSet exe) { super(distribution, config, runtimeConfig, exe); this.runtimeConfig = runtimeConfig; + addShutdownHook(); } @Override @@ -24,4 +35,48 @@ protected PostgresProcess start(Distribution distribution, PostgresConfig config throws IOException { return new PostgresProcess(distribution, config, runtime, this); } -} \ No newline at end of file + + private void addShutdownHook() { + ProcessControl.addShutdownHook(new CleanerRunner(SubdirTempDir.defaultInstance())); + } + + static class CleanerRunner implements Runnable { + + private IDirectory directory; + + CleanerRunner(IDirectory directory) { + this.directory = directory; + } + + @Override + public void run() { + DirectoryCleaner.getInstance().clean(this.directory.asFile()); + } + } + + static class DirectoryCleaner { + private static Logger logger = LoggerFactory.getLogger(DirectoryCleaner.class); + private static final DirectoryCleaner instance = new DirectoryCleaner(); + + static DirectoryCleaner getInstance() { + return instance; + } + + void clean(File cleanupTarget) { + synchronized (instance) { + 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); + } + } + } + } +} From f976dbdabcea644899411134e2b0f911cf90531f Mon Sep 17 00:00:00 2001 From: fu-taku Date: Thu, 23 Aug 2018 13:39:09 +0900 Subject: [PATCH 2/5] Add PowerMock --- pom.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pom.xml b/pom.xml index 923df3f..6c1a2aa 100644 --- a/pom.xml +++ b/pom.xml @@ -186,6 +186,18 @@ ${mockito.version} test + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + org.powermock + powermock-api-mockito + ${powermock.version} + test + org.hamcrest hamcrest-all @@ -226,6 +238,7 @@ 1.10.19 + 1.7.4 4.12 1.3 4.3.2.RELEASE From 1f1961288bfd0f07aef055f2cd27dd0c5f76b8a4 Mon Sep 17 00:00:00 2001 From: fu-taku Date: Thu, 23 Aug 2018 13:39:31 +0900 Subject: [PATCH 3/5] Add test code --- .../TestDeleteTemporaryDirectory.java | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 src/test/java/ru/yandex/qatools/embed/postgresql/TestDeleteTemporaryDirectory.java 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..ae65a92 --- /dev/null +++ b/src/test/java/ru/yandex/qatools/embed/postgresql/TestDeleteTemporaryDirectory.java @@ -0,0 +1,105 @@ +package ru.yandex.qatools.embed.postgresql; + +import de.flapdoodle.embed.process.io.directories.FixedPath; +import de.flapdoodle.embed.process.io.directories.IDirectory; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +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(PowerMockRunner.class) +@PrepareForTest({PostgresExecutable.DirectoryCleaner.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); + PowerMockito.mockStatic(PostgresExecutable.DirectoryCleaner.class); + PowerMockito.when(PostgresExecutable.DirectoryCleaner.getInstance()).thenReturn(cleaner); + PowerMockito.doNothing().when(cleaner).clean(Mockito.any(File.class)); + + // Create test directory path + IDirectory directory = new FixedPath(testDirectory.toFile().getAbsolutePath()); + + // Run CleanerRunner + PostgresExecutable.CleanerRunner runner = new PostgresExecutable.CleanerRunner(directory); + runner.run(); + + // Clean method was called by CleanerRunner + Mockito.verify(cleaner, Mockito.times(1)).clean(directory.asFile()); + } + + @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")); + } +} From e6ba85e1b3be02c8213d0f76044d773276dd72e4 Mon Sep 17 00:00:00 2001 From: fu-taku Date: Fri, 24 Aug 2018 11:55:40 +0900 Subject: [PATCH 4/5] Get temporary directory path from runtime config --- .../embed/postgresql/PostgresExecutable.java | 21 ++++++++----------- .../TestDeleteTemporaryDirectory.java | 10 +++------ 2 files changed, 12 insertions(+), 19 deletions(-) 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 d5af0bd..b8ed5fd 100644 --- a/src/main/java/ru/yandex/qatools/embed/postgresql/PostgresExecutable.java +++ b/src/main/java/ru/yandex/qatools/embed/postgresql/PostgresExecutable.java @@ -3,12 +3,10 @@ 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.io.directories.IDirectory; import de.flapdoodle.embed.process.runtime.ProcessControl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ru.yandex.qatools.embed.postgresql.config.PostgresConfig; -import ru.yandex.qatools.embed.postgresql.ext.SubdirTempDir; import java.io.File; import java.io.IOException; @@ -21,36 +19,35 @@ * 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; - addShutdownHook(); } @Override protected PostgresProcess start(Distribution distribution, PostgresConfig config, IRuntimeConfig runtime) throws IOException { + addShutdownHook(runtime, distribution); return new PostgresProcess(distribution, config, runtime, this); } - private void addShutdownHook() { - ProcessControl.addShutdownHook(new CleanerRunner(SubdirTempDir.defaultInstance())); + private void addShutdownHook(IRuntimeConfig runtimeConfig, Distribution distribution) throws IOException { + ProcessControl.addShutdownHook( + new CleanerRunner(runtimeConfig.getArtifactStore().extractFileSet(distribution).baseDir()) + ); } static class CleanerRunner implements Runnable { - private IDirectory directory; + private File fileOrDirectory; - CleanerRunner(IDirectory directory) { - this.directory = directory; + CleanerRunner(File fileOrDirectory) { + this.fileOrDirectory = fileOrDirectory; } @Override public void run() { - DirectoryCleaner.getInstance().clean(this.directory.asFile()); + DirectoryCleaner.getInstance().clean(this.fileOrDirectory); } } diff --git a/src/test/java/ru/yandex/qatools/embed/postgresql/TestDeleteTemporaryDirectory.java b/src/test/java/ru/yandex/qatools/embed/postgresql/TestDeleteTemporaryDirectory.java index ae65a92..0090354 100644 --- a/src/test/java/ru/yandex/qatools/embed/postgresql/TestDeleteTemporaryDirectory.java +++ b/src/test/java/ru/yandex/qatools/embed/postgresql/TestDeleteTemporaryDirectory.java @@ -1,7 +1,5 @@ package ru.yandex.qatools.embed.postgresql; -import de.flapdoodle.embed.process.io.directories.FixedPath; -import de.flapdoodle.embed.process.io.directories.IDirectory; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -60,15 +58,13 @@ public void callCleanMethodWhenCleanerRunnerIsRun() { PowerMockito.when(PostgresExecutable.DirectoryCleaner.getInstance()).thenReturn(cleaner); PowerMockito.doNothing().when(cleaner).clean(Mockito.any(File.class)); - // Create test directory path - IDirectory directory = new FixedPath(testDirectory.toFile().getAbsolutePath()); - // Run CleanerRunner - PostgresExecutable.CleanerRunner runner = new PostgresExecutable.CleanerRunner(directory); + PostgresExecutable.CleanerRunner runner = + new PostgresExecutable.CleanerRunner(testDirectory.toFile()); runner.run(); // Clean method was called by CleanerRunner - Mockito.verify(cleaner, Mockito.times(1)).clean(directory.asFile()); + Mockito.verify(cleaner, Mockito.times(1)).clean(testDirectory.toFile()); } @Test From d4d9a3704bb1ee5c2b79da143ed134822f503325 Mon Sep 17 00:00:00 2001 From: fu-taku Date: Fri, 2 Nov 2018 11:17:57 +0900 Subject: [PATCH 5/5] Remove PowerMock --- pom.xml | 13 ------------- .../embed/postgresql/PostgresExecutable.java | 14 +++++++------- .../postgresql/TestDeleteTemporaryDirectory.java | 14 +++++--------- 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/pom.xml b/pom.xml index 6c1a2aa..923df3f 100644 --- a/pom.xml +++ b/pom.xml @@ -186,18 +186,6 @@ ${mockito.version} test - - org.powermock - powermock-module-junit4 - ${powermock.version} - test - - - org.powermock - powermock-api-mockito - ${powermock.version} - test - org.hamcrest hamcrest-all @@ -238,7 +226,6 @@ 1.10.19 - 1.7.4 4.12 1.3 4.3.2.RELEASE 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 b8ed5fd..c935b98 100644 --- a/src/main/java/ru/yandex/qatools/embed/postgresql/PostgresExecutable.java +++ b/src/main/java/ru/yandex/qatools/embed/postgresql/PostgresExecutable.java @@ -45,22 +45,22 @@ static class CleanerRunner implements Runnable { this.fileOrDirectory = fileOrDirectory; } + // TODO インスタンス生成するのはここで良いのか?もっと違うやり方があるのでは? + DirectoryCleaner getCleaner() { + return new DirectoryCleaner(); + } + @Override public void run() { - DirectoryCleaner.getInstance().clean(this.fileOrDirectory); + getCleaner().clean(this.fileOrDirectory); } } static class DirectoryCleaner { private static Logger logger = LoggerFactory.getLogger(DirectoryCleaner.class); - private static final DirectoryCleaner instance = new DirectoryCleaner(); - - static DirectoryCleaner getInstance() { - return instance; - } void clean(File cleanupTarget) { - synchronized (instance) { + synchronized (DirectoryCleaner.class) { if (!cleanupTarget.exists()) { return; } diff --git a/src/test/java/ru/yandex/qatools/embed/postgresql/TestDeleteTemporaryDirectory.java b/src/test/java/ru/yandex/qatools/embed/postgresql/TestDeleteTemporaryDirectory.java index 0090354..38b2ed6 100644 --- a/src/test/java/ru/yandex/qatools/embed/postgresql/TestDeleteTemporaryDirectory.java +++ b/src/test/java/ru/yandex/qatools/embed/postgresql/TestDeleteTemporaryDirectory.java @@ -5,9 +5,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import org.mockito.runners.MockitoJUnitRunner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,8 +20,7 @@ import static org.junit.Assert.assertFalse; -@RunWith(PowerMockRunner.class) -@PrepareForTest({PostgresExecutable.DirectoryCleaner.class}) +@RunWith(MockitoJUnitRunner.class) public class TestDeleteTemporaryDirectory { private static final Logger logger = LoggerFactory.getLogger(TestDeleteTemporaryDirectory.class); @@ -54,13 +51,12 @@ public void cleanup() { public void callCleanMethodWhenCleanerRunnerIsRun() { // Mock DirectoryCleaner PostgresExecutable.DirectoryCleaner cleaner = Mockito.spy(PostgresExecutable.DirectoryCleaner.class); - PowerMockito.mockStatic(PostgresExecutable.DirectoryCleaner.class); - PowerMockito.when(PostgresExecutable.DirectoryCleaner.getInstance()).thenReturn(cleaner); - PowerMockito.doNothing().when(cleaner).clean(Mockito.any(File.class)); + Mockito.doNothing().when(cleaner).clean(Mockito.any(File.class)); // Run CleanerRunner PostgresExecutable.CleanerRunner runner = - new PostgresExecutable.CleanerRunner(testDirectory.toFile()); + Mockito.spy(new PostgresExecutable.CleanerRunner(testDirectory.toFile())); + Mockito.doReturn(cleaner).when(runner).getCleaner(); runner.run(); // Clean method was called by CleanerRunner