This is an automated email from the ASF dual-hosted git repository. sdanilov pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push: new b1128c227e IGNITE-18970 Allow WorkDirectoryExtension to preserve work directories of specific tests (#1753) b1128c227e is described below commit b1128c227edc07489a5045c269466f6c1c544407 Author: Semyon Danilov <samvi...@yandex.ru> AuthorDate: Tue Mar 7 14:10:20 2023 +0400 IGNITE-18970 Allow WorkDirectoryExtension to preserve work directories of specific tests (#1753) --- .../testframework/WorkDirectoryExtensionTest.java | 135 ++++++++++++++++++++- .../testframework/WorkDirectoryExtension.java | 111 ++++++++++++++--- 2 files changed, 228 insertions(+), 18 deletions(-) diff --git a/modules/core/src/test/java/org/apache/ignite/internal/testframework/WorkDirectoryExtensionTest.java b/modules/core/src/test/java/org/apache/ignite/internal/testframework/WorkDirectoryExtensionTest.java index d56f78ef1e..0ab278b651 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/testframework/WorkDirectoryExtensionTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/testframework/WorkDirectoryExtensionTest.java @@ -19,6 +19,7 @@ package org.apache.ignite.internal.testframework; import static org.apache.ignite.internal.testframework.JunitExtensionTestUtils.assertExecutesSuccessfully; import static org.apache.ignite.internal.testframework.JunitExtensionTestUtils.assertExecutesWithFailure; +import static org.apache.ignite.internal.testframework.WorkDirectoryExtension.keepWorkDirPropertyValid; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.not; @@ -31,8 +32,10 @@ import static org.junit.platform.testkit.engine.TestExecutionResultConditions.me import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashSet; import java.util.Set; +import org.apache.ignite.internal.util.IgniteUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -204,16 +207,33 @@ class WorkDirectoryExtensionTest { private static Path file2; + private static Path file3; + + private static final String TMP_PATH; + + static { + try { + TMP_PATH = Files.createTempDirectory("testdir").toString(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + @AfterAll - static void verify() throws IOException { + static void verify() { assertTrue(Files.exists(file1)); assertFalse(Files.exists(file2)); + assertFalse(Files.exists(file3)); + assertTrue(Files.exists(Paths.get(TMP_PATH))); - Files.delete(file1); + IgniteUtils.deleteIfExists(file1.getParent()); + IgniteUtils.deleteIfExists(Paths.get(TMP_PATH)); + + System.clearProperty(WorkDirectoryExtension.ARTIFACT_DIR_PROPERTY); } @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") - @WithSystemProperty(key = WorkDirectoryExtension.KEEP_WORK_DIR_PROPERTY, value = "true") + @WithSystemProperty(key = WorkDirectoryExtension.KEEP_WORK_DIR_PROPERTY, value = "SystemPropertiesTest.test1") @Test void test1(@WorkDirectory Path workDir) throws IOException { file1 = Files.createFile(workDir.resolve("foo")); @@ -224,6 +244,83 @@ class WorkDirectoryExtensionTest { void test2(@WorkDirectory Path workDir) throws IOException { file2 = Files.createFile(workDir.resolve("foo")); } + + @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") + @WithSystemProperty(key = WorkDirectoryExtension.KEEP_WORK_DIR_PROPERTY, value = "SystemPropertiesTest.test3") + @Test + void test3(@WorkDirectory Path workDir) throws IOException { + file3 = Files.createFile(workDir.resolve("foo")); + + System.setProperty(WorkDirectoryExtension.ARTIFACT_DIR_PROPERTY, TMP_PATH); + } + } + + /** + * Test class for the {@link #testSystemPropertyWithStaticWorkDir()} test. + */ + @ExtendWith(SystemPropertiesExtension.class) + @ExtendWith(WorkDirectoryExtension.class) + @WithSystemProperty(key = WorkDirectoryExtension.KEEP_WORK_DIR_PROPERTY, value = "SystemPropertiesTestWithStaticWorkDir") + static class SystemPropertiesTestWithStaticWorkDir { + private static Path file1; + + private static Path file2; + + @WorkDirectory + static Path workDir; + + @AfterAll + static void verify() { + assertTrue(Files.exists(file1)); + assertTrue(Files.exists(file2)); + + IgniteUtils.deleteIfExists(file1.getParent()); + } + + @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") + @Test + void test1() throws IOException { + file1 = Files.createFile(workDir.resolve("foo")); + } + + @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") + @Test + void test2() throws IOException { + file2 = Files.createFile(workDir.resolve("bar")); + } + } + + /** + * Test class for the {@link #testSystemPropertyWithMultipleTests()} test. + */ + @ExtendWith(SystemPropertiesExtension.class) + @ExtendWith(WorkDirectoryExtension.class) + @WithSystemProperty(key = WorkDirectoryExtension.KEEP_WORK_DIR_PROPERTY, value = "SystemPropertiesTestWithMultipleTests.test1," + + "SystemPropertiesTestWithMultipleTests.test2") + static class SystemPropertiesTestWithMultipleTests { + private static Path file1; + + private static Path file2; + + @AfterAll + static void verify() { + assertTrue(Files.exists(file1)); + assertTrue(Files.exists(file2)); + + IgniteUtils.deleteIfExists(file1.getParent()); + } + + @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") + @Test + void test1(@WorkDirectory Path workDir) throws IOException { + file1 = Files.createFile(workDir.resolve("foo")); + } + + @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") + @Test + void test2(@WorkDirectory Path workDir) throws IOException { + file2 = Files.createFile(workDir.resolve("bar")); + } } /** @@ -234,6 +331,38 @@ class WorkDirectoryExtensionTest { assertExecutesSuccessfully(SystemPropertiesTest.class); } + /** + * Tests that a static work directory can be preserved when a special system property is set. + */ + @Test + void testSystemPropertyWithStaticWorkDir() { + assertExecutesSuccessfully(SystemPropertiesTestWithMultipleTests.class); + } + + /** + * Tests that a static work directory can be preserved when a special system property is set. + */ + @Test + void testSystemPropertyWithMultipleTests() { + assertExecutesSuccessfully(SystemPropertiesTestWithMultipleTests.class); + } + + /** + * Tests {@link WorkDirectoryExtension#keepWorkDirPropertyValid}. + */ + @Test + void testKeepWorkDirectoryPattern() { + assertTrue(keepWorkDirPropertyValid("Foo")); + assertTrue(keepWorkDirPropertyValid("Foo.bar")); + assertTrue(keepWorkDirPropertyValid("Foo,Foo")); + assertTrue(keepWorkDirPropertyValid("Foo.bar,Foo")); + assertTrue(keepWorkDirPropertyValid("Foo.bar,Foo.bar")); + + assertFalse(keepWorkDirPropertyValid("Foo#bar")); + assertFalse(keepWorkDirPropertyValid("Foo.bar, Foo")); + assertFalse(keepWorkDirPropertyValid("Foo ,Foo.bar")); + } + /** * Test class for the {@link #testEmptyClass()} test. */ diff --git a/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/WorkDirectoryExtension.java b/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/WorkDirectoryExtension.java index 213f160d87..ef45afd5fe 100644 --- a/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/WorkDirectoryExtension.java +++ b/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/WorkDirectoryExtension.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.testframework; +import static java.util.stream.Collectors.toSet; import static org.apache.ignite.internal.testframework.IgniteTestUtils.isWindowsOs; import static org.junit.jupiter.api.extension.ExtensionContext.Namespace; @@ -26,9 +27,16 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.concurrent.ThreadLocalRandom; +import java.util.regex.Pattern; import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import org.apache.ignite.internal.util.IgniteUtils; import org.apache.ignite.lang.IgniteSystemProperties; import org.jetbrains.annotations.Nullable; @@ -61,8 +69,8 @@ import org.junit.platform.commons.support.HierarchyTraversalMode; * </ol> * * <p>Temporary folders are removed after tests have finished running, but this behaviour can be controlled by setting the - * {@link WorkDirectoryExtension#KEEP_WORK_DIR_PROPERTY} property to {@code true}, in which case the created folder can - * be kept intact for debugging purposes. + * {@link WorkDirectoryExtension#KEEP_WORK_DIR_PROPERTY} property. See {@link #KEEP_WORK_DIR_PROPERTY} and {@link #ARTIFACT_DIR_PROPERTY} + * for more information. */ public class WorkDirectoryExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, ParameterResolver { @@ -70,16 +78,30 @@ public class WorkDirectoryExtension private static final Namespace NAMESPACE = Namespace.create(WorkDirectoryExtension.class); /** - * System property that, when set to {@code true}, will make the extension preserve the created directories. Default is {@code false}. + * System property that can be used to provide a comma-separated list of test names whose work directories should be preserved after + * test execution. Test name consists of test class name, a dot and a test method name. In case if work directory is injected into the + * static field and is shared between different tests, only test class name should be put into the list. + * <br> + * Example: {@code "FooBarTest.test1,FooBarTest.test2,StaticWorkDirectoryFieldTest,FooBarTest.test3"}. + * Default value is {@code null}. */ public static final String KEEP_WORK_DIR_PROPERTY = "KEEP_WORK_DIR"; + /** + * System property that denotes the directory where archived work directory of the test, kept using {@link #KEEP_WORK_DIR_PROPERTY}, + * should be saved in. If defined, original work directory will be deleted. Default value is {@code null}. + */ + public static final String ARTIFACT_DIR_PROPERTY = "ARTIFACT_DIR"; + /** Base path for all temporary folders in a module. */ private static final Path BASE_PATH = getBasePath(); /** Name of the work directory that will be injected into {@link BeforeAll} methods or static members. */ private static final String STATIC_FOLDER_NAME = "static"; + /** Pattern for the {@link #KEEP_WORK_DIR_PROPERTY}. */ + private static final Pattern PATTERN = Pattern.compile("\\b\\w+(?:\\.\\w+)?(?:,\\b\\w+(?:\\.\\w+)?)*\\b"); + /** * Creates and injects a temporary directory into a static field. */ @@ -99,7 +121,7 @@ public class WorkDirectoryExtension /** {@inheritDoc} */ @Override public void afterAll(ExtensionContext context) throws Exception { - removeWorkDir(context); + cleanupWorkDir(context); Path testClassDir = getTestClassDir(context); @@ -133,7 +155,7 @@ public class WorkDirectoryExtension /** {@inheritDoc} */ @Override public void afterEach(ExtensionContext context) throws Exception { - removeWorkDir(context); + cleanupWorkDir(context); } /** {@inheritDoc} */ @@ -212,11 +234,77 @@ public class WorkDirectoryExtension /** * Removes a previously created work directory. */ - private static void removeWorkDir(ExtensionContext context) { + private static void cleanupWorkDir(ExtensionContext context) { Path workDir = context.getStore(NAMESPACE).remove(context.getUniqueId(), Path.class); - if (workDir != null && shouldRemoveDir()) { - IgniteUtils.deleteIfExists(workDir); + String testClassName = context.getRequiredTestClass().getSimpleName(); + + String testName = context.getTestMethod().map(method -> testClassName + "." + method.getName()).orElse(testClassName); + + if (workDir != null) { + if (shouldKeepWorkDir(testName)) { + String artifactDir = IgniteSystemProperties.getString(ARTIFACT_DIR_PROPERTY); + + if (artifactDir != null) { + Path artifactDirPath = Paths.get(artifactDir, testName + ".zip"); + + zipDirectory(workDir, artifactDirPath); + + IgniteUtils.deleteIfExists(workDir); + } + } else { + IgniteUtils.deleteIfExists(workDir); + } + } + } + + private static boolean shouldKeepWorkDir(String testName) { + String keepWorkDirStr = IgniteSystemProperties.getString(KEEP_WORK_DIR_PROPERTY); + + Set<String> keepWorkDirForTests; + + if (keepWorkDirStr != null) { + if (!keepWorkDirPropertyValid(keepWorkDirStr)) { + throw new IllegalArgumentException(KEEP_WORK_DIR_PROPERTY + " value " + keepWorkDirStr + " doesn't match pattern"); + } + + keepWorkDirForTests = Arrays.stream(keepWorkDirStr.split(",")).collect(toSet()); + } else { + keepWorkDirForTests = Collections.emptySet(); + } + + return keepWorkDirForTests.contains(testName); + } + + static boolean keepWorkDirPropertyValid(String property) { + return PATTERN.matcher(property).matches(); + } + + private static void zipDirectory(Path source, Path target) { + try { + Files.createDirectories(target.getParent()); + + Files.createFile(target); + + try (var zs = new ZipOutputStream(Files.newOutputStream(target))) { + try (Stream<Path> filesStream = Files.walk(source)) { + filesStream.filter(path -> !Files.isDirectory(path)) + .forEach(path -> { + var zipEntry = new ZipEntry(source.relativize(path).toString()); + try { + zs.putNextEntry(zipEntry); + + Files.copy(path, zs); + + zs.closeEntry(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + } + } catch (IOException e) { + throw new RuntimeException(e); } } @@ -250,13 +338,6 @@ public class WorkDirectoryExtension return fields.get(0); } - /** - * Returns {@code true} if the extension should remove the created directories. - */ - private static boolean shouldRemoveDir() { - return !IgniteSystemProperties.getBoolean(KEEP_WORK_DIR_PROPERTY); - } - /** * Returns {@code true} if the given directory is empty or {@code false} if the given directory contains files or does not exist. */