This is an automated email from the ASF dual-hosted git repository. klund pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push: new 755eaaa GEODE-4929: Add ability for tests to overide the default DiskDirs (#1701) 755eaaa is described below commit 755eaaa56685a185de56e1bdc3c33d04ad4420dc Author: Kirk Lund <kl...@apache.org> AuthorDate: Fri Mar 30 09:37:33 2018 -0700 GEODE-4929: Add ability for tests to overide the default DiskDirs (#1701) * System property geode.defaultDiskDirs is used to specify some dir other than "." * DiskDirRule can be used by IntegrationTests * DistributedDiskDirRule can be used by DistributedTests * Update BucketRegionSizeWithOverflowRegressionTest to use DistributedDiskDirRule --- .../org/apache/geode/cache/AttributesFactory.java | 12 +- .../geode/internal/cache/DiskStoreAttributes.java | 3 +- .../apache/geode/internal/cache/LocalRegion.java | 3 +- .../cache/persistence/DefaultDiskDirs.java | 38 +++ .../internal/cache/xmlcache/CacheXmlGenerator.java | 3 +- .../geode/internal/lang/SystemPropertyHelper.java | 21 ++ ...a => PRAccessorWithOverflowRegressionTest.java} | 29 +-- .../DefaultDiskDirsIntegrationTest.java | 47 ++++ .../cache/persistence/DefaultDiskDirsTest.java | 50 ++++ .../test/dunit/rules/DistributedDiskDirRule.java | 275 +++++++++++++++++++++ .../apache/geode/test/junit/rules/DiskDirRule.java | 188 ++++++++++++++ .../junit/rules/DescribedExternalResource.java | 23 +- .../serializable/SerializableTemporaryFolder.java | 8 +- .../rules/serializable/SerializableTestName.java | 2 +- 14 files changed, 656 insertions(+), 46 deletions(-) diff --git a/geode-core/src/main/java/org/apache/geode/cache/AttributesFactory.java b/geode-core/src/main/java/org/apache/geode/cache/AttributesFactory.java index f9f07bf..64e427a 100644 --- a/geode-core/src/main/java/org/apache/geode/cache/AttributesFactory.java +++ b/geode-core/src/main/java/org/apache/geode/cache/AttributesFactory.java @@ -34,6 +34,7 @@ import org.apache.geode.internal.cache.EvictionAttributesImpl; import org.apache.geode.internal.cache.PartitionAttributesImpl; import org.apache.geode.internal.cache.PartitionedRegionHelper; import org.apache.geode.internal.cache.UserSpecifiedRegionAttributes; +import org.apache.geode.internal.cache.persistence.DefaultDiskDirs; import org.apache.geode.internal.cache.xmlcache.RegionAttributesCreation; import org.apache.geode.internal.i18n.LocalizedStrings; @@ -1541,18 +1542,15 @@ public class AttributesFactory<K, V> { boolean publisher = false; boolean enableAsyncConflation = false; boolean enableSubscriptionConflation = false; - @SuppressWarnings("deprecation") DiskWriteAttributes diskWriteAttributes = DiskWriteAttributesImpl.getDefaultSyncInstance(); - File[] diskDirs = DiskStoreFactory.DEFAULT_DISK_DIRS; - int[] diskSizes = new int[] {DiskStoreFactory.DEFAULT_DISK_DIR_SIZE}; // 10* 1024 MB } + File[] diskDirs = DefaultDiskDirs.getDefaultDiskDirs(); + int[] diskSizes = DiskStoreFactory.DEFAULT_DISK_DIR_SIZES; boolean indexMaintenanceSynchronous = true; - PartitionAttributes partitionAttributes = null; // new PartitionAttributes(); + PartitionAttributes partitionAttributes = null; MembershipAttributes membershipAttributes = new MembershipAttributes(); SubscriptionAttributes subscriptionAttributes = new SubscriptionAttributes(); boolean multicastEnabled = false; - EvictionAttributesImpl evictionAttributes = new EvictionAttributesImpl(); // TODO need to - // determine the - // constructor + EvictionAttributesImpl evictionAttributes = new EvictionAttributesImpl(); String poolName = null; String diskStoreName = null; boolean diskSynchronous = DEFAULT_DISK_SYNCHRONOUS; diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/DiskStoreAttributes.java b/geode-core/src/main/java/org/apache/geode/internal/cache/DiskStoreAttributes.java index 90b48c9..f5965e2 100644 --- a/geode-core/src/main/java/org/apache/geode/internal/cache/DiskStoreAttributes.java +++ b/geode-core/src/main/java/org/apache/geode/internal/cache/DiskStoreAttributes.java @@ -23,6 +23,7 @@ import java.util.UUID; import org.apache.geode.cache.DiskStore; import org.apache.geode.cache.DiskStoreFactory; +import org.apache.geode.internal.cache.persistence.DefaultDiskDirs; /** * Creates an attribute object for DiskStore. @@ -61,7 +62,7 @@ public class DiskStoreAttributes implements Serializable, DiskStore { this.timeInterval = DiskStoreFactory.DEFAULT_TIME_INTERVAL; this.writeBufferSize = DiskStoreFactory.DEFAULT_WRITE_BUFFER_SIZE; this.queueSize = DiskStoreFactory.DEFAULT_QUEUE_SIZE; - this.diskDirs = DiskStoreFactory.DEFAULT_DISK_DIRS; + this.diskDirs = DefaultDiskDirs.getDefaultDiskDirs(); this.diskDirSizes = DiskStoreFactory.DEFAULT_DISK_DIR_SIZES; this.diskUsageWarningPct = DiskStoreFactory.DEFAULT_DISK_USAGE_WARNING_PERCENTAGE; this.diskUsageCriticalPct = DiskStoreFactory.DEFAULT_DISK_USAGE_CRITICAL_PERCENTAGE; diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/LocalRegion.java b/geode-core/src/main/java/org/apache/geode/internal/cache/LocalRegion.java index a1ae0a5..f12f1af 100644 --- a/geode-core/src/main/java/org/apache/geode/internal/cache/LocalRegion.java +++ b/geode-core/src/main/java/org/apache/geode/internal/cache/LocalRegion.java @@ -186,6 +186,7 @@ import org.apache.geode.internal.cache.execute.ServerToClientFunctionResultSende import org.apache.geode.internal.cache.ha.ThreadIdentifier; import org.apache.geode.internal.cache.partitioned.Bucket; import org.apache.geode.internal.cache.partitioned.RedundancyAlreadyMetException; +import org.apache.geode.internal.cache.persistence.DefaultDiskDirs; import org.apache.geode.internal.cache.persistence.DiskExceptionHandler; import org.apache.geode.internal.cache.persistence.DiskRecoveryStore; import org.apache.geode.internal.cache.persistence.DiskRegionView; @@ -7526,7 +7527,7 @@ public class LocalRegion extends AbstractRegion implements LoaderHelperFactory, */ private boolean useDefaultDiskStore() { assert getDiskStoreName() == null; - if (!Arrays.equals(getDiskDirs(), DiskStoreFactory.DEFAULT_DISK_DIRS)) { + if (!Arrays.equals(getDiskDirs(), DefaultDiskDirs.getDefaultDiskDirs())) { return false; } if (!Arrays.equals(getDiskDirSizes(), DiskStoreFactory.DEFAULT_DISK_DIR_SIZES)) { diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/persistence/DefaultDiskDirs.java b/geode-core/src/main/java/org/apache/geode/internal/cache/persistence/DefaultDiskDirs.java new file mode 100644 index 0000000..991e4c7 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/cache/persistence/DefaultDiskDirs.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.geode.internal.cache.persistence; + +import static org.apache.geode.internal.lang.SystemPropertyHelper.DEFAULT_DISK_DIRS_PROPERTY; +import static org.apache.geode.internal.lang.SystemPropertyHelper.getProductStringProperty; + +import java.io.File; +import java.util.Optional; + +public class DefaultDiskDirs { + + static File[] DEFAULT_DISK_DIRS_VALUE = new File[] {new File(".")}; + + public static File[] getDefaultDiskDirs() { + Optional<String> value = getProductStringProperty(DEFAULT_DISK_DIRS_PROPERTY); + if (value.isPresent()) { + String diskDirs = value.get(); + return new File[] {new File(diskDirs)}; + } else { + return DEFAULT_DISK_DIRS_VALUE; + } + } +} diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/CacheXmlGenerator.java b/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/CacheXmlGenerator.java index 1bdb64c..58832d2 100644 --- a/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/CacheXmlGenerator.java +++ b/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/CacheXmlGenerator.java @@ -123,6 +123,7 @@ import org.apache.geode.internal.cache.PartitionedRegion; import org.apache.geode.internal.cache.control.MemoryThresholds; import org.apache.geode.internal.cache.extension.Extensible; import org.apache.geode.internal.cache.extension.Extension; +import org.apache.geode.internal.cache.persistence.DefaultDiskDirs; import org.apache.geode.internal.i18n.LocalizedStrings; import org.apache.geode.internal.size.SizeClassOnceObjectSizer; import org.apache.geode.management.internal.configuration.utils.XmlConstants; @@ -1140,7 +1141,7 @@ public class CacheXmlGenerator extends CacheXml implements XMLReader { File[] diskDirs = ds.getDiskDirs(); int[] diskSizes = ds.getDiskDirSizes(); if (diskDirs != null && diskDirs.length > 0) { - if (generateDefaults() || !Arrays.equals(diskDirs, DiskStoreFactory.DEFAULT_DISK_DIRS) + if (generateDefaults() || !Arrays.equals(diskDirs, DefaultDiskDirs.getDefaultDiskDirs()) || !Arrays.equals(diskSizes, DiskStoreFactory.DEFAULT_DISK_DIR_SIZES)) { handler.startElement("", DISK_DIRS, DISK_DIRS, EMPTY); for (int i = 0; i < diskDirs.length; i++) { diff --git a/geode-core/src/main/java/org/apache/geode/internal/lang/SystemPropertyHelper.java b/geode-core/src/main/java/org/apache/geode/internal/lang/SystemPropertyHelper.java index 5208044..fc2551d 100644 --- a/geode-core/src/main/java/org/apache/geode/internal/lang/SystemPropertyHelper.java +++ b/geode-core/src/main/java/org/apache/geode/internal/lang/SystemPropertyHelper.java @@ -59,6 +59,8 @@ public class SystemPropertyHelper { public static final String EARLY_ENTRY_EVENT_SERIALIZATION = "earlyEntryEventSerialization"; + public static final String DEFAULT_DISK_DIRS_PROPERTY = "defaultDiskDirs"; + /** * This method will try to look up "geode." and "gemfire." versions of the system property. It * will check and prefer "geode." setting first, then try to check "gemfire." setting. @@ -71,6 +73,13 @@ public class SystemPropertyHelper { return property != null ? Optional.of(Boolean.parseBoolean(property)) : Optional.empty(); } + /** + * This method will try to look up "geode." and "gemfire." versions of the system property. It + * will check and prefer "geode." setting first, then try to check "gemfire." setting. + * + * @param name system property name set in Geode + * @return an Optional containing the Integer value of the system property + */ public static Optional<Integer> getProductIntegerProperty(String name) { Integer propertyValue = Integer.getInteger(GEODE_PREFIX + name); if (propertyValue == null) { @@ -84,6 +93,18 @@ public class SystemPropertyHelper { } } + /** + * This method will try to look up "geode." and "gemfire." versions of the system property. It + * will check and prefer "geode." setting first, then try to check "gemfire." setting. + * + * @param name system property name set in Geode + * @return an Optional containing the String value of the system property + */ + public static Optional<String> getProductStringProperty(String name) { + String property = getProperty(name); + return property != null ? Optional.of(property) : Optional.empty(); + } + private static String getProperty(String name) { String property = getGeodeProperty(name); return property != null ? property : getGemfireProperty(name); diff --git a/geode-core/src/test/java/org/apache/geode/internal/cache/BucketRegionSizeWithOverflowRegressionTest.java b/geode-core/src/test/java/org/apache/geode/internal/cache/PRAccessorWithOverflowRegressionTest.java similarity index 79% rename from geode-core/src/test/java/org/apache/geode/internal/cache/BucketRegionSizeWithOverflowRegressionTest.java rename to geode-core/src/test/java/org/apache/geode/internal/cache/PRAccessorWithOverflowRegressionTest.java index 807966c..efb0f02 100644 --- a/geode-core/src/test/java/org/apache/geode/internal/cache/BucketRegionSizeWithOverflowRegressionTest.java +++ b/geode-core/src/test/java/org/apache/geode/internal/cache/PRAccessorWithOverflowRegressionTest.java @@ -25,20 +25,18 @@ import java.io.File; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.apache.geode.cache.AttributesFactory; import org.apache.geode.cache.DataPolicy; -import org.apache.geode.cache.DiskStoreFactory; import org.apache.geode.cache.PartitionAttributesFactory; import org.apache.geode.cache.Region; import org.apache.geode.test.dunit.VM; import org.apache.geode.test.dunit.cache.CacheTestCase; +import org.apache.geode.test.dunit.rules.DistributedDiskDirRule; import org.apache.geode.test.junit.categories.DistributedTest; -import org.apache.geode.test.junit.rules.serializable.SerializableTemporaryFolder; import org.apache.geode.test.junit.rules.serializable.SerializableTestName; /** @@ -46,13 +44,9 @@ import org.apache.geode.test.junit.rules.serializable.SerializableTestName; * * <p> * TRAC #42055: a pr accessor configured for OverflowToDisk fails during creation because of disk - * - * <p> - * TRAC #42055 also mentions delta so we should add test(s) for delta as well. <br> - * TODO: Test that the bucket size does not go negative when we fault out and in a delta object. */ @Category(DistributedTest.class) -public class BucketRegionSizeWithOverflowRegressionTest extends CacheTestCase { +public class PRAccessorWithOverflowRegressionTest extends CacheTestCase { private static final int ENTRIES_COUNT = 1; @@ -64,10 +58,10 @@ public class BucketRegionSizeWithOverflowRegressionTest extends CacheTestCase { private VM accessor; @Rule - public SerializableTemporaryFolder temporaryFolder = new SerializableTemporaryFolder(); + public SerializableTestName testName = new SerializableTestName(); @Rule - public SerializableTestName testName = new SerializableTestName(); + public DistributedDiskDirRule diskDirsRule = new DistributedDiskDirRule(); @Before public void setUp() throws Exception { @@ -75,8 +69,9 @@ public class BucketRegionSizeWithOverflowRegressionTest extends CacheTestCase { accessor = getHost(0).getVM(1); uniqueName = getClass().getSimpleName() + "_" + testName.getMethodName(); - datastoreDiskDir = temporaryFolder.newFolder(uniqueName + "_datastore_disk"); - accessorDiskDir = temporaryFolder.newFolder(uniqueName + "_accessor_disk"); + + datastoreDiskDir = diskDirsRule.getDiskDirFor(datastore); + accessorDiskDir = diskDirsRule.getDiskDirFor(accessor); datastore.invoke(() -> createDataStore()); accessor.invoke(() -> createAccessor()); @@ -87,7 +82,6 @@ public class BucketRegionSizeWithOverflowRegressionTest extends CacheTestCase { disconnectAllFromDS(); } - @Ignore("GEODE-4929") @Test public void testPROverflow() throws Exception { accessor.invoke(() -> { @@ -104,7 +98,6 @@ public class BucketRegionSizeWithOverflowRegressionTest extends CacheTestCase { datastore.invoke(() -> { PartitionedRegion partitionedRegion = (PartitionedRegion) getCache().getRegion(uniqueName); assertThat(getCache().getRegion(uniqueName).size()).isEqualTo(2); - assertThat(getCache().getRegion(uniqueName).size()).isGreaterThanOrEqualTo(0); assertThat(partitionedRegion.getDataStore().getAllLocalBucketIds()).hasSize(2); }); @@ -117,12 +110,8 @@ public class BucketRegionSizeWithOverflowRegressionTest extends CacheTestCase { } private void createDataStore() { - DiskStoreFactory dsf = getCache().createDiskStoreFactory(); - dsf.setDiskDirs(new File[] {datastoreDiskDir}); - AttributesFactory af = new AttributesFactory(); af.setDataPolicy(DataPolicy.PARTITION); - af.setDiskStoreName(dsf.create(uniqueName).getName()); af.setEvictionAttributes(createLRUEntryAttributes(ENTRIES_COUNT, OVERFLOW_TO_DISK)); af.setPartitionAttributes(new PartitionAttributesFactory().create()); @@ -130,15 +119,11 @@ public class BucketRegionSizeWithOverflowRegressionTest extends CacheTestCase { } private void createAccessor() { - DiskStoreFactory dsf = getCache().createDiskStoreFactory(); - dsf.setDiskDirs(new File[] {accessorDiskDir}); - PartitionAttributesFactory<Integer, TestDelta> paf = new PartitionAttributesFactory<>(); paf.setLocalMaxMemory(0); AttributesFactory<Integer, TestDelta> af = new AttributesFactory<>(); af.setDataPolicy(DataPolicy.PARTITION); - af.setDiskStoreName(dsf.create(uniqueName).getName()); af.setEvictionAttributes(createLRUEntryAttributes(ENTRIES_COUNT, OVERFLOW_TO_DISK)); af.setPartitionAttributes(paf.create()); diff --git a/geode-core/src/test/java/org/apache/geode/internal/cache/persistence/DefaultDiskDirsIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/internal/cache/persistence/DefaultDiskDirsIntegrationTest.java new file mode 100644 index 0000000..aadda75 --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/internal/cache/persistence/DefaultDiskDirsIntegrationTest.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.geode.internal.cache.persistence; + +import static org.apache.geode.internal.cache.persistence.DefaultDiskDirs.getDefaultDiskDirs; +import static org.apache.geode.internal.lang.SystemPropertyHelper.DEFAULT_DISK_DIRS_PROPERTY; +import static org.apache.geode.internal.lang.SystemPropertyHelper.GEODE_PREFIX; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.RestoreSystemProperties; +import org.junit.experimental.categories.Category; +import org.junit.rules.TemporaryFolder; + +import org.apache.geode.test.junit.categories.IntegrationTest; + +@Category(IntegrationTest.class) +public class DefaultDiskDirsIntegrationTest { + + @Rule + public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void getDefaultDiskDirsReturnsOverriddenValue() { + System.setProperty(GEODE_PREFIX + DEFAULT_DISK_DIRS_PROPERTY, + temporaryFolder.getRoot().getAbsolutePath()); + assertThat(getDefaultDiskDirs()[0]).exists().isEqualTo(temporaryFolder.getRoot()); + } +} diff --git a/geode-core/src/test/java/org/apache/geode/internal/cache/persistence/DefaultDiskDirsTest.java b/geode-core/src/test/java/org/apache/geode/internal/cache/persistence/DefaultDiskDirsTest.java new file mode 100644 index 0000000..c445bca --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/internal/cache/persistence/DefaultDiskDirsTest.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.geode.internal.cache.persistence; + +import static org.apache.geode.internal.cache.persistence.DefaultDiskDirs.DEFAULT_DISK_DIRS_VALUE; +import static org.apache.geode.internal.cache.persistence.DefaultDiskDirs.getDefaultDiskDirs; +import static org.apache.geode.internal.lang.SystemPropertyHelper.DEFAULT_DISK_DIRS_PROPERTY; +import static org.apache.geode.internal.lang.SystemPropertyHelper.GEODE_PREFIX; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.RestoreSystemProperties; +import org.junit.experimental.categories.Category; + +import org.apache.geode.test.junit.categories.UnitTest; + +@Category(UnitTest.class) +public class DefaultDiskDirsTest { + + @Rule + public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); + + @Test + public void getDefaultDiskDirsReturnsTheDefault() throws Exception { + assertThat(getDefaultDiskDirs()).isEqualTo(DEFAULT_DISK_DIRS_VALUE); + } + + @Test + public void getDefaultDiskDirsReturnsOverriddenValue() { + System.setProperty(GEODE_PREFIX + DEFAULT_DISK_DIRS_PROPERTY, "/FullyQualifiedPath"); + assertThat(getDefaultDiskDirs()).isEqualTo(new File[] {new File("/FullyQualifiedPath")}); + } +} diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedDiskDirRule.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedDiskDirRule.java new file mode 100644 index 0000000..1ada67b --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedDiskDirRule.java @@ -0,0 +1,275 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.geode.test.dunit.rules; + +import static org.apache.geode.internal.lang.SystemPropertyHelper.DEFAULT_DISK_DIRS_PROPERTY; +import static org.apache.geode.internal.lang.SystemPropertyHelper.GEODE_PREFIX; +import static org.apache.geode.internal.lang.SystemPropertyHelper.getProductStringProperty; +import static org.apache.geode.test.dunit.Host.getHost; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.lang.reflect.Method; +import java.util.Optional; + +import org.junit.rules.TemporaryFolder; +import org.junit.rules.TestName; +import org.junit.runner.Description; + +import org.apache.geode.test.dunit.VM; +import org.apache.geode.test.junit.rules.DiskDirRule; +import org.apache.geode.test.junit.rules.serializable.SerializableTemporaryFolder; +import org.apache.geode.test.junit.rules.serializable.SerializableTestName; +import org.apache.geode.test.junit.rules.serializable.SerializableTestRule; + +/** + * JUnit Rule that overrides the default DiskDirs directory in all DUnit VMs (except for the hidden + * Locator VM). Internally, SerializableTemporaryFolder and SerializableTestName are used by this + * rule to define the directory locations and names. + * + * <p> + * You may either pass in instances of SerializableTemporaryFolder and SerializableTestName from + * the test or the DistributedDiskDirRule will create its own instances. Either way, it will invoke + * SerializableTemporaryFolder.before and SerializableTestName.starting(Description). If the test + * provides its own instances of these rules defined, please do not annotate these instances with + * {@code @Rule}. + * + * <p> + * Each JVM will have its own default DiskDirs directory in which that JVM will create the default + * disk store (if one is created). Each DiskDir name is defined as: + * + * <pre> + * "VM" + VM.getCurrentVMNum() + "-" + testClass + "_" + testName.getMethodName() + "-diskDirs" + * </pre> + * + * Using DistributedDiskDirRule will produce unique DiskDirs for each DUnit VM including the main + * controller VM (-1) but not the locator VM (-2): + * + * <pre> + * /var/folders/28/m__9dv1906n60kmz7t71wm680000gn/T/junit1766147044000254810 + * VM-1-PRAccessorWithOverflowRegressionTest_testPROverflow-diskDirs + * VM0-PRAccessorWithOverflowRegressionTest_testPROverflow-diskDirs + * VM1-PRAccessorWithOverflowRegressionTest_testPROverflow-diskDirs + * VM2-PRAccessorWithOverflowRegressionTest_testPROverflow-diskDirs + * VM3-PRAccessorWithOverflowRegressionTest_testPROverflow-diskDirs + * </pre> + * + * <p> + * Example of test using DistributedDiskDirRule: + * + * <pre> + * {@literal @}Category(DistributedTest.class) + * public class PRAccessorWithOverflowRegressionTest extends CacheTestCase { + * + * {@literal @}Rule + * public DistributedDiskDirRule diskDirsRule = new DistributedDiskDirRule(); + * </pre> + */ +@SuppressWarnings("serial,unused") +public class DistributedDiskDirRule extends DiskDirRule implements SerializableTestRule { + + private static volatile DistributedDiskDirRuleData data; + + private final SerializableTemporaryFolder temporaryFolder; + private final SerializableTestName testName; + private final RemoteInvoker invoker; + + private volatile int beforeVmCount; + + public DistributedDiskDirRule() { + this(new Builder()); + } + + public DistributedDiskDirRule(SerializableTemporaryFolder temporaryFolder) { + this(new Builder().temporaryFolder(temporaryFolder)); + } + + public DistributedDiskDirRule(SerializableTestName testName) { + this(new Builder().testName(testName)); + } + + public DistributedDiskDirRule(SerializableTemporaryFolder temporaryFolder, + SerializableTestName testName) { + this(new Builder().temporaryFolder(temporaryFolder).testName(testName)); + } + + public DistributedDiskDirRule(Builder builder) { + this(builder.fillIn(), new RemoteInvoker()); + } + + protected DistributedDiskDirRule(Builder builder, RemoteInvoker invoker) { + super(builder.initializeHelperRules, null, null); + temporaryFolder = builder.temporaryFolder; + testName = builder.testName; + this.invoker = invoker; + } + + public File getDiskDirFor(VM vm) { + return new File(vm.invoke(() -> System.getProperty(GEODE_PREFIX + DEFAULT_DISK_DIRS_PROPERTY))); + } + + @Override + protected void before(Description description) throws Exception { + beforeVmCount = getVMCount(); + + if (initializeHelperRules) { + initializeHelperRules(description); + } + + invoker.invokeInEveryVMAndController(() -> doBefore(this, description)); + } + + @Override + protected void initializeHelperRules(Description description) throws Exception { + if (temporaryFolder != null) { + Method method = TemporaryFolder.class.getDeclaredMethod(BEFORE); + method.setAccessible(true); + method.invoke(temporaryFolder); + } + + if (testName != null) { + Method method = TestName.class.getDeclaredMethod(STARTING, Description.class); + method.setAccessible(true); + method.invoke(testName, description); + } + } + + @Override + protected void after(Description description) { + assertThat(getVMCount()).isEqualTo(beforeVmCount); + + invoker.invokeInEveryVMAndController(() -> doAfter()); + } + + @Override + protected String getDiskDirName(String testClass) { + return "VM" + VM.getCurrentVMNum() + "-" + testClass + "_" + testName.getMethodName() + + "-diskDirs"; + } + + private void doBefore(DistributedDiskDirRule diskDirRule, Description description) + throws Exception { + data = new DistributedDiskDirRuleData(diskDirRule); + + Optional<String> value = getProductStringProperty(DEFAULT_DISK_DIRS_PROPERTY); + value.ifPresent(s -> data.setOriginalValue(s)); + + File diskDir = data.temporaryFolder().newFolder(getDiskDirName(getTestClassName(description))); + + System.setProperty(GEODE_PREFIX + DEFAULT_DISK_DIRS_PROPERTY, diskDir.getAbsolutePath()); + } + + private void doAfter() { + if (data.originalValue() == null) { + System.clearProperty(GEODE_PREFIX + DEFAULT_DISK_DIRS_PROPERTY); + } else { + System.setProperty(GEODE_PREFIX + DEFAULT_DISK_DIRS_PROPERTY, data.originalValue()); + } + } + + private int getVMCount() { + try { + return getHost(0).getVMCount(); + } catch (IllegalArgumentException e) { + throw new IllegalStateException("DUnit VMs have not been launched"); + } + } + + /** + * Data for DistributedDiskDirRule for each DUnit child VM. + */ + private static class DistributedDiskDirRuleData { + private final SerializableTemporaryFolder temporaryFolder; + private final SerializableTestName testName; + + private volatile String originalValue; + + DistributedDiskDirRuleData(DistributedDiskDirRule diskDirRule) { + this(diskDirRule.temporaryFolder, diskDirRule.testName); + } + + private DistributedDiskDirRuleData(SerializableTemporaryFolder temporaryFolder, + SerializableTestName testName) { + this.temporaryFolder = temporaryFolder; + this.testName = testName; + } + + SerializableTemporaryFolder temporaryFolder() { + return temporaryFolder; + } + + SerializableTestName testName() { + return testName; + } + + String originalValue() { + return originalValue; + } + + void setOriginalValue(String originalValue) { + this.originalValue = originalValue; + } + } + + /** + * Builds an instance of DistributedDiskDirRule + */ + public static class Builder { + private boolean initializeHelperRules = true; + private SerializableTemporaryFolder temporaryFolder; + private SerializableTestName testName; + + public Builder() { + // nothing + } + + /** + * Specify false to disable initializing SerializableTemporaryFolder and SerializableTestName + * during DistributedDiskDirRule initialization. If this is enabled then do NOT annotate these + * helper rules in the test or combine them with RuleChain or RuleList. Default value is true. + */ + public Builder initializeHelperRules(boolean value) { + initializeHelperRules = value; + return this; + } + + public Builder temporaryFolder(SerializableTemporaryFolder temporaryFolder) { + this.temporaryFolder = temporaryFolder; + return this; + } + + public Builder testName(SerializableTestName testName) { + this.testName = testName; + return this; + } + + public DistributedDiskDirRule build() { + fillIn(); + return new DistributedDiskDirRule(this); + } + + private Builder fillIn() { + if (temporaryFolder == null) { + temporaryFolder = new SerializableTemporaryFolder(); + } + if (testName == null) { + testName = new SerializableTestName(); + } + return this; + } + } +} diff --git a/geode-core/src/test/java/org/apache/geode/test/junit/rules/DiskDirRule.java b/geode-core/src/test/java/org/apache/geode/test/junit/rules/DiskDirRule.java new file mode 100644 index 0000000..bbeec89 --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/test/junit/rules/DiskDirRule.java @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.geode.test.junit.rules; + +import static org.apache.geode.internal.lang.SystemPropertyHelper.DEFAULT_DISK_DIRS_PROPERTY; +import static org.apache.geode.internal.lang.SystemPropertyHelper.GEODE_PREFIX; +import static org.apache.geode.internal.lang.SystemPropertyHelper.getProductStringProperty; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Optional; + +import org.junit.rules.TemporaryFolder; +import org.junit.rules.TestName; +import org.junit.runner.Description; + +@SuppressWarnings("unused") +public class DiskDirRule extends DescribedExternalResource { + + protected static final String BEFORE = "before"; + protected static final String AFTER = "after"; + protected static final String STARTING = "starting"; + + protected final boolean initializeHelperRules; + + private final TemporaryFolder temporaryFolder; + private final TestName testName; + + private String originalValue; + + public DiskDirRule(TemporaryFolder temporaryFolder) { + this(new Builder().temporaryFolder(temporaryFolder)); + } + + public DiskDirRule(TestName testName) { + this(new Builder().testName(testName)); + } + + public DiskDirRule(TemporaryFolder temporaryFolder, TestName testName) { + this(new Builder().temporaryFolder(temporaryFolder).testName(testName)); + } + + public DiskDirRule(Builder builder) { + this(builder.initializeHelperRules, builder.temporaryFolder, builder.testName); + } + + protected DiskDirRule() { + this(false, null, null); + } + + protected DiskDirRule(boolean initializeHelperRules, TemporaryFolder temporaryFolder, + TestName testName) { + this.initializeHelperRules = initializeHelperRules; + this.temporaryFolder = temporaryFolder; + this.testName = testName; + } + + @Override + protected void before(Description description) throws Exception { + Optional<String> value = getProductStringProperty(DEFAULT_DISK_DIRS_PROPERTY); + value.ifPresent(s -> originalValue = s); + + if (initializeHelperRules) { + initializeHelperRules(description); + } + + File diskDir = + temporaryFolder.newFolder(getDiskDirName(getDiskDirName(description.getClassName()))); + + System.setProperty(GEODE_PREFIX + DEFAULT_DISK_DIRS_PROPERTY, diskDir.getAbsolutePath()); + } + + protected void initializeHelperRules(Description description) throws Exception { + if (temporaryFolder != null) { + Method method = TemporaryFolder.class.getDeclaredMethod(BEFORE); + method.setAccessible(true); + method.invoke(temporaryFolder); + } + + if (testName != null) { + Method method = TestName.class.getDeclaredMethod(STARTING, Description.class); + method.setAccessible(true); + method.invoke(testName, description); + } + } + + @Override + protected void after(Description description) { + if (originalValue == null) { + System.clearProperty(GEODE_PREFIX + DEFAULT_DISK_DIRS_PROPERTY); + } else { + System.setProperty(GEODE_PREFIX + DEFAULT_DISK_DIRS_PROPERTY, originalValue); + } + } + + protected String getDiskDirName(String testClass) { + return testClass + "_" + testName.getMethodName() + "-diskDirs"; + } + + protected String getTestClassName(Description description) { + return description.getTestClass().getSimpleName(); + } + + protected void invokeTemporaryFolderBefore(TemporaryFolder temporaryFolder) { + if (temporaryFolder != null) { + invoke(TemporaryFolder.class, temporaryFolder, BEFORE); + } + } + + protected void invokeTestNameBefore(TestName testName) { + if (testName != null) { + invoke(TestName.class, testName, BEFORE); + } + } + + protected <V> V invoke(Class<?> targetClass, Object targetInstance, String methodName) { + try { + Method method = targetClass.getDeclaredMethod(methodName); + method.setAccessible(true); + return (V) method.invoke(targetInstance); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new Error(e); + } + } + + /** + * Builds an instance of DiskDirRule + */ + public static class Builder { + private boolean initializeHelperRules = true; + private TemporaryFolder temporaryFolder; + private TestName testName; + + public Builder() { + // nothing + } + + /** + * Specify false to disable initializing TemporaryFolder and TestName during DiskDirRule + * initialization. If this is enabled then do NOT annotate these helper rules in the test or + * combine them with RuleChain or RuleList. Default value is true. + */ + public Builder initializeHelperRules(boolean value) { + initializeHelperRules = value; + return this; + } + + public Builder temporaryFolder(TemporaryFolder temporaryFolder) { + this.temporaryFolder = temporaryFolder; + return this; + } + + public Builder testName(TestName testName) { + this.testName = testName; + return this; + } + + public DiskDirRule build() { + fillIn(); + return new DiskDirRule(this); + } + + private Builder fillIn() { + if (temporaryFolder == null) { + temporaryFolder = new TemporaryFolder(); + } + if (testName == null) { + testName = new TestName(); + } + return this; + } + } +} diff --git a/geode-junit/src/main/java/org/apache/geode/test/junit/rules/DescribedExternalResource.java b/geode-junit/src/main/java/org/apache/geode/test/junit/rules/DescribedExternalResource.java index f8d4668..106c713 100644 --- a/geode-junit/src/main/java/org/apache/geode/test/junit/rules/DescribedExternalResource.java +++ b/geode-junit/src/main/java/org/apache/geode/test/junit/rules/DescribedExternalResource.java @@ -19,16 +19,19 @@ import org.junit.runner.Description; import org.junit.runners.model.Statement; /** - * this class extends the capability of JUnit's ExternalResource in that it provides a Description - * object in the before and after methods, so that the implementation would have access to the - * annotation of the test methods + * A base class for Rules that require {@code Description} to set up an external resource before + * a test and tear it down afterward. {@code DescribedExternalResource} is similar to + * {@code ExternalResource} but includes {@code Description} as a parameter to both {@code before} + * and {@code after}. + * + * <p> + * {@code Description} allows the implementation to have access to the test class, its annotations + * and information about JUnit lifecycle. */ public abstract class DescribedExternalResource implements TestRule { - public Statement apply(Statement base, Description description) { - return statement(base, description); - } - private Statement statement(final Statement base, final Description description) { + @Override + public Statement apply(Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { @@ -42,18 +45,18 @@ public abstract class DescribedExternalResource implements TestRule { }; } - /** * Override to set up your specific external resource. * - * @throws Throwable if setup fails (which will disable {@code after} + * @throws Throwable if setup fails (which will prevent the invocation of {@code after}) */ protected void before(Description description) throws Throwable { // do nothing } /** - * Override to tear down your specific external resource. + * Override to tear down your specific external resource. Note: ExternalResource after + * does not include {@code throws Throwable}. */ protected void after(Description description) throws Throwable { // do nothing diff --git a/geode-junit/src/main/java/org/apache/geode/test/junit/rules/serializable/SerializableTemporaryFolder.java b/geode-junit/src/main/java/org/apache/geode/test/junit/rules/serializable/SerializableTemporaryFolder.java index 3d55336..a7cf565 100755 --- a/geode-junit/src/main/java/org/apache/geode/test/junit/rules/serializable/SerializableTemporaryFolder.java +++ b/geode-junit/src/main/java/org/apache/geode/test/junit/rules/serializable/SerializableTemporaryFolder.java @@ -14,8 +14,10 @@ */ package org.apache.geode.test.junit.rules.serializable; -import static org.apache.geode.test.junit.rules.serializable.FieldSerializationUtils.*; -import static org.apache.geode.test.junit.rules.serializable.FieldsOfTemporaryFolder.*; +import static org.apache.geode.test.junit.rules.serializable.FieldSerializationUtils.readField; +import static org.apache.geode.test.junit.rules.serializable.FieldSerializationUtils.writeField; +import static org.apache.geode.test.junit.rules.serializable.FieldsOfTemporaryFolder.FIELD_FOLDER; +import static org.apache.geode.test.junit.rules.serializable.FieldsOfTemporaryFolder.FIELD_PARENT_FOLDER; import java.io.File; import java.io.InvalidObjectException; @@ -25,7 +27,7 @@ import java.io.Serializable; import org.junit.rules.TemporaryFolder; /** - * Serializable subclass of {@link org.junit.rules.TemporaryFolder TemporaryFolder}. Instance + * Serializable subclass of {@link TemporaryFolder TemporaryFolder}. Instance * variables of TemporaryFolder are serialized by reflection. */ public class SerializableTemporaryFolder extends TemporaryFolder implements SerializableTestRule { diff --git a/geode-junit/src/main/java/org/apache/geode/test/junit/rules/serializable/SerializableTestName.java b/geode-junit/src/main/java/org/apache/geode/test/junit/rules/serializable/SerializableTestName.java index 437ca17..bb3436d 100755 --- a/geode-junit/src/main/java/org/apache/geode/test/junit/rules/serializable/SerializableTestName.java +++ b/geode-junit/src/main/java/org/apache/geode/test/junit/rules/serializable/SerializableTestName.java @@ -25,7 +25,7 @@ import org.junit.rules.TestName; import org.junit.runner.Description; /** - * Serializable subclass of {@link org.junit.rules.TestName TestName}. All instance variables of + * Serializable subclass of {@link TestName TestName}. All instance variables of * {@code TestName} are serialized by reflection. */ public class SerializableTestName extends TestName implements SerializableTestRule { -- To stop receiving notification emails like this one, please contact kl...@apache.org.