/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * Licensed 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 com.hazelcast.test; import classloading.ThreadLocalLeakTestUtils; import com.hazelcast.client.impl.ClientEngineImpl; import com.hazelcast.cluster.ClusterState; import com.hazelcast.config.Config; import com.hazelcast.core.Cluster; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.Member; import com.hazelcast.core.Partition; import com.hazelcast.core.PartitionService; import com.hazelcast.instance.HazelcastInstanceFactory; import com.hazelcast.instance.Node; import com.hazelcast.instance.NodeExtension; import com.hazelcast.instance.TestUtil; import com.hazelcast.internal.cluster.ClusterService; import com.hazelcast.internal.metrics.MetricsRegistry; import com.hazelcast.internal.partition.InternalPartitionService; import com.hazelcast.internal.partition.impl.PartitionServiceState; import com.hazelcast.internal.serialization.InternalSerializationService; import com.hazelcast.map.impl.operation.DefaultMapOperationProvider; import com.hazelcast.map.impl.operation.MapOperation; import com.hazelcast.map.impl.operation.MapOperationProvider; import com.hazelcast.nio.Address; import com.hazelcast.nio.ConnectionManager; import com.hazelcast.nio.Packet; import com.hazelcast.spi.NodeEngine; import com.hazelcast.spi.Operation; import com.hazelcast.spi.impl.NodeEngineImpl; import com.hazelcast.spi.impl.operationparker.impl.OperationParkerImpl; import com.hazelcast.spi.impl.operationservice.InternalOperationService; import com.hazelcast.spi.impl.operationservice.impl.OperationServiceImpl; import com.hazelcast.spi.partition.IPartition; import com.hazelcast.spi.serialization.SerializationService; import com.hazelcast.test.jitter.JitterRule; import org.junit.After; import org.junit.ComparisonFailure; import org.junit.Rule; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import static com.hazelcast.test.TestPartitionUtils.getPartitionServiceState; import static com.hazelcast.util.ExceptionUtil.rethrow; import static java.lang.Integer.getInteger; import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.UUID.randomUUID; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @SuppressWarnings({"unused", "SameParameterValue", "WeakerAccess"}) public abstract class HazelcastTestSupport { public static final int ASSERT_TRUE_EVENTUALLY_TIMEOUT; @Rule public JitterRule jitterRule = new JitterRule(); static { ASSERT_TRUE_EVENTUALLY_TIMEOUT = getInteger("hazelcast.assertTrueEventually.timeout", 120); System.out.println("ASSERT_TRUE_EVENTUALLY_TIMEOUT = " + ASSERT_TRUE_EVENTUALLY_TIMEOUT); } private TestHazelcastInstanceFactory factory; @After public final void shutdownNodeFactory() { TestHazelcastInstanceFactory testHazelcastInstanceFactory = factory; if (testHazelcastInstanceFactory != null) { factory = null; testHazelcastInstanceFactory.terminateAll(); } } // ################################### // ########## configuration ########## // ################################### protected Config getConfig() { return new Config(); } // ############################################### // ########## HazelcastInstance factory ########## // ############################################### protected HazelcastInstance createHazelcastInstance() { return createHazelcastInstance(getConfig()); } protected HazelcastInstance createHazelcastInstance(Config config) { return createHazelcastInstanceFactory(1).newHazelcastInstance(config); } protected final TestHazelcastInstanceFactory createHazelcastInstanceFactory(int nodeCount) { if (factory != null) { throw new IllegalStateException("Node factory is already created!"); } return factory = new TestHazelcastInstanceFactory(nodeCount); } protected final TestHazelcastInstanceFactory createHazelcastInstanceFactory(String... addresses) { if (factory != null) { throw new IllegalStateException("Node factory is already created!"); } return factory = new TestHazelcastInstanceFactory(addresses); } protected final TestHazelcastInstanceFactory createHazelcastInstanceFactory() { if (factory != null) { throw new IllegalStateException("Node factory is already created!"); } return factory = new TestHazelcastInstanceFactory(); } protected final TestHazelcastInstanceFactory createHazelcastInstanceFactory(int initialPort, String... addresses) { if (factory != null) { throw new IllegalStateException("Node factory is already created!"); } return factory = new TestHazelcastInstanceFactory(initialPort, addresses); } // ########################################### // ########## implementation getter ########## // ########################################### public static Node getNode(HazelcastInstance hz) { return TestUtil.getNode(hz); } public static NodeEngineImpl getNodeEngineImpl(HazelcastInstance hz) { return getNode(hz).nodeEngine; } public static ClientEngineImpl getClientEngineImpl(HazelcastInstance instance) { return getNode(instance).clientEngine; } public static ConnectionManager getConnectionManager(HazelcastInstance hz) { return getNode(hz).connectionManager; } public static ClusterService getClusterService(HazelcastInstance hz) { return getNode(hz).clusterService; } public static InternalPartitionService getPartitionService(HazelcastInstance hz) { return getNode(hz).partitionService; } public static InternalSerializationService getSerializationService(HazelcastInstance hz) { return getNode(hz).getSerializationService(); } public static InternalOperationService getOperationService(HazelcastInstance hz) { return getNodeEngineImpl(hz).getOperationService(); } public static OperationServiceImpl getOperationServiceImpl(HazelcastInstance hz) { return (OperationServiceImpl) getNodeEngineImpl(hz).getOperationService(); } public static MetricsRegistry getMetricsRegistry(HazelcastInstance hz) { return getNodeEngineImpl(hz).getMetricsRegistry(); } public static Address getAddress(HazelcastInstance hz) { return getClusterService(hz).getThisAddress(); } public static Packet toPacket(HazelcastInstance local, HazelcastInstance remote, Operation operation) { InternalSerializationService serializationService = getSerializationService(local); ConnectionManager connectionManager = getConnectionManager(local); return new Packet(serializationService.toBytes(operation), operation.getPartitionId()) .setPacketType(Packet.Type.OPERATION) .setConn(connectionManager.getConnection(getAddress(remote))); } // ##################################### // ########## generic utility ########## // ##################################### protected void checkThreadLocalsForLeaks() throws Exception { ThreadLocalLeakTestUtils.checkThreadLocalsForLeaks(getClass().getClassLoader()); } public static void ignore(Throwable ignored) { } public static Future spawn(Runnable task) { FutureTask<Runnable> futureTask = new FutureTask<Runnable>(task, null); new Thread(futureTask).start(); return futureTask; } public static <E> Future<E> spawn(Callable<E> task) { FutureTask<E> futureTask = new FutureTask<E>(task); new Thread(futureTask).start(); return futureTask; } public static void interruptCurrentThread(final int delayMillis) { final Thread currentThread = Thread.currentThread(); new Thread(new Runnable() { public void run() { sleepMillis(delayMillis); currentThread.interrupt(); } }).start(); } public static void printAllStackTraces() { StringBuilder sb = new StringBuilder(); Map liveThreads = Thread.getAllStackTraces(); for (Object object : liveThreads.keySet()) { Thread key = (Thread) object; sb.append("Thread ").append(key.getName()); StackTraceElement[] trace = (StackTraceElement[]) liveThreads.get(key); for (StackTraceElement aTrace : trace) { sb.append("\tat ").append(aTrace); } } System.err.println(sb.toString()); } // ########################### // ########## sleep ########## // ########################### public static void sleepMillis(int millis) { try { MILLISECONDS.sleep(millis); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } public static void sleepSeconds(int seconds) { try { TimeUnit.SECONDS.sleep(seconds); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } /** * Sleeps for the given amount of time and after that, sets stop to true. * * If stop is changed to true while sleeping, the calls returns before waiting the full sleeping period. * * This method is very useful for stress tests that run for a certain amount of time. But if one of the stress tests * runs into a failure, the test should be aborted immediately. This is done by letting the thread set stop to true. * * @param stop an {@link AtomicBoolean} to stop the sleep method * @param durationSeconds sleep duration in seconds */ public static void sleepAndStop(AtomicBoolean stop, long durationSeconds) { for (int i = 0; i < durationSeconds; i++) { if (stop.get()) { return; } sleepSeconds(1); } stop.set(true); } public static void sleepAtLeastMillis(long sleepFor) { boolean interrupted = false; try { long remainingNanos = MILLISECONDS.toNanos(sleepFor); long sleepUntil = System.nanoTime() + remainingNanos; while (remainingNanos > 0) { try { NANOSECONDS.sleep(remainingNanos); } catch (InterruptedException e) { interrupted = true; } finally { remainingNanos = sleepUntil - System.nanoTime(); } } } finally { if (interrupted) { Thread.currentThread().interrupt(); } } } public static void sleepAtLeastSeconds(long seconds) { sleepAtLeastMillis(seconds * 1000); } // ####################################### // ########## random generators ########## // ####################################### public static String generateRandomString(int length) { StringBuilder sb = new StringBuilder(length); Random random = new Random(); for (int i = 0; i < length; i++) { char character = (char) (random.nextInt(26) + 'a'); sb.append(character); } return sb.toString(); } public static String randomString() { return randomUUID().toString(); } public static String randomMapName() { return randomString(); } public static String randomMapName(String namePrefix) { return namePrefix + randomString(); } public static String randomName() { return randomString(); } // ######################################################### // ########## HazelcastInstance random generators ########## // ######################################################### public static String randomNameOwnedBy(HazelcastInstance hz) { return randomNameOwnedBy(hz, ""); } public static String randomNameOwnedBy(HazelcastInstance instance, String prefix) { Member localMember = instance.getCluster().getLocalMember(); PartitionService partitionService = instance.getPartitionService(); while (true) { String id = prefix + randomString(); Partition partition = partitionService.getPartition(id); if (comparePartitionOwnership(true, localMember, partition)) { return id; } } } public static Partition randomPartitionOwnedBy(HazelcastInstance hz) { List<Partition> partitions = new LinkedList<Partition>(); for (Partition partition : hz.getPartitionService().getPartitions()) { if (partition.getOwner().localMember()) { partitions.add(partition); } } if (partitions.isEmpty()) { throw new IllegalStateException("No partitions found for HazelcastInstance:" + hz.getName()); } return partitions.get((int) (Math.random() * partitions.size())); } public static String generateKeyOwnedBy(HazelcastInstance instance) { return generateKeyOwnedBy(instance, true); } public static String generateKeyNotOwnedBy(HazelcastInstance instance) { return generateKeyOwnedBy(instance, false); } /** * Generates a key according to given reference instance by checking partition ownership for it. * * @param instance reference instance for key generation. * @param generateOwnedKey {@code true} if we want a key which is owned by the given instance, otherwise * set to {@code false} which means generated key will not be owned by the given instance. * @return generated string. */ public static String generateKeyOwnedBy(HazelcastInstance instance, boolean generateOwnedKey) { Cluster cluster = instance.getCluster(); checkMemberCount(generateOwnedKey, cluster); checkPartitionCountGreaterOrEqualMemberCount(instance); Member localMember = cluster.getLocalMember(); PartitionService partitionService = instance.getPartitionService(); while (true) { String id = randomString(); Partition partition = partitionService.getPartition(id); if (comparePartitionOwnership(generateOwnedKey, localMember, partition)) { return id; } } } public static String generateKeyForPartition(HazelcastInstance instance, int partitionId) { Cluster cluster = instance.getCluster(); checkPartitionCountGreaterOrEqualMemberCount(instance); Member localMember = cluster.getLocalMember(); PartitionService partitionService = instance.getPartitionService(); while (true) { String id = randomString(); Partition partition = partitionService.getPartition(id); if (partition.getPartitionId() == partitionId) { return id; } } } public String[] generateKeysBelongingToSamePartitionsOwnedBy(HazelcastInstance instance, int keyCount) { int partitionId = getPartitionId(instance); String[] keys = new String[keyCount]; for (int i = 0; i < keys.length; i++) { keys[i] = generateKeyForPartition(instance, partitionId); } return keys; } private static boolean comparePartitionOwnership(boolean ownedBy, Member member, Partition partition) { Member owner = partition.getOwner(); if (ownedBy) { return member.equals(owner); } else { return !member.equals(owner); } } private static void checkPartitionCountGreaterOrEqualMemberCount(HazelcastInstance instance) { Cluster cluster = instance.getCluster(); int memberCount = cluster.getMembers().size(); InternalPartitionService internalPartitionService = getPartitionService(instance); int partitionCount = internalPartitionService.getPartitionCount(); if (partitionCount < memberCount) { throw new UnsupportedOperationException("Partition count should be equal or greater than member count!"); } } // ################################## // ########## partition id ########## // ################################## /** * Gets a partition id owned by this particular member. */ public static int getPartitionId(HazelcastInstance hz) { warmUpPartitions(hz); InternalPartitionService partitionService = getPartitionService(hz); for (IPartition partition : partitionService.getPartitions()) { if (partition.isLocal()) { return partition.getPartitionId(); } } throw new RuntimeException("No local partitions are found for hz: " + hz.getName()); } public static int getPartitionId(HazelcastInstance hz, String partitionName) { PartitionService partitionService = hz.getPartitionService(); Partition partition = partitionService.getPartition(partitionName); return partition.getPartitionId(); } // ################################################ // ########## cluster and instance state ########## // ################################################ public static void closeConnectionBetween(HazelcastInstance h1, HazelcastInstance h2) { if (h1 == null || h2 == null) { return; } Node n1 = TestUtil.getNode(h1); Node n2 = TestUtil.getNode(h2); suspectMember(n1, n2); suspectMember(n2, n1); } public static void suspectMember(Node suspectingNode, Node suspectedNode, String reason) { if (suspectingNode != null && suspectedNode != null) { Member suspectedMember = suspectingNode.getClusterService().getMember(suspectedNode.getLocalMember().getAddress()); if (suspectedMember != null) { suspectingNode.clusterService.suspectMember(suspectedMember, reason, true); } } } public static void suspectMember(Node suspectingNode, Node suspectedNode) { suspectMember(suspectingNode, suspectedNode, null); } private static void checkMemberCount(boolean generateOwnedKey, Cluster cluster) { if (generateOwnedKey) { return; } Set<Member> members = cluster.getMembers(); if (members.size() < 2) { throw new UnsupportedOperationException("Cluster has only one member, you can not generate a `not owned key`"); } } public static void warmUpPartitions(HazelcastInstance... instances) { TestUtil.warmUpPartitions(instances); } public static void warmUpPartitions(Collection<HazelcastInstance> instances) { TestUtil.warmUpPartitions(instances); } public static boolean isInstanceInSafeState(HazelcastInstance instance) { Node node = TestUtil.getNode(instance); if (node == null) { return true; } InternalPartitionService ps = node.getPartitionService(); return ps.isMemberStateSafe(); } public static boolean isClusterInSafeState(HazelcastInstance instance) { PartitionService ps = instance.getPartitionService(); return ps.isClusterSafe(); } public static boolean isAllInSafeState() { Set<HazelcastInstance> nodeSet = HazelcastInstanceFactory.getAllHazelcastInstances(); return isAllInSafeState(nodeSet); } public static boolean isAllInSafeState(Collection<HazelcastInstance> nodes) { for (HazelcastInstance node : nodes) { if (!isInstanceInSafeState(node)) { return false; } } return true; } public static void waitInstanceForSafeState(final HazelcastInstance instance) { assertTrueEventually(new AssertTask() { public void run() { isInstanceInSafeState(instance); } }); } public static void waitClusterForSafeState(final HazelcastInstance instance) { assertTrueEventually(new AssertTask() { public void run() { assertTrue(isClusterInSafeState(instance)); } }); } public static void waitUntilClusterState(HazelcastInstance hz, ClusterState state, int timeoutSeconds) { int waited = 0; while (!hz.getCluster().getClusterState().equals(state)) { if (waited++ == timeoutSeconds) { break; } sleepSeconds(1); } } public static void waitAllForSafeState(Collection<HazelcastInstance> instances) { waitAllForSafeState(instances, ASSERT_TRUE_EVENTUALLY_TIMEOUT); } public static void waitAllForSafeState(final Collection<HazelcastInstance> instances, int timeoutInSeconds) { assertTrueEventually(new AssertTask() { public void run() { assertAllInSafeState(instances); } }, timeoutInSeconds); } public static void waitAllForSafeState(final HazelcastInstance[] nodes, int timeoutInSeconds) { assertTrueEventually(new AssertTask() { public void run() { assertAllInSafeState(asList(nodes)); } }, timeoutInSeconds); } public static void waitAllForSafeState(HazelcastInstance... nodes) { if (nodes.length == 0) { throw new IllegalArgumentException("waitAllForSafeState(HazelcastInstance... nodes) cannot be called with" + " an empty array. It's too easy to mistake it for the old argument-less waitAllForSafeState()." + " The old version was removed as it was implemented via Hazelcast.getAllHazelcastInstances()" + " which uses a static map internally and it's causing issues when tests are running in parallel."); } waitAllForSafeState(asList(nodes)); } public static void assertAllInSafeState(Collection<HazelcastInstance> nodes) { Map<Address, PartitionServiceState> nonSafeStates = new HashMap<Address, PartitionServiceState>(); for (HazelcastInstance node : nodes) { if (node == null) { continue; } final PartitionServiceState state = getPartitionServiceState(node); if (state != PartitionServiceState.SAFE) { nonSafeStates.put(getAddress(node), state); } } assertTrue("Instances not in safe state! " + nonSafeStates, nonSafeStates.isEmpty()); } public static void assertNodeStarted(HazelcastInstance instance) { NodeExtension nodeExtension = getNode(instance).getNodeExtension(); assertTrue(nodeExtension.isStartCompleted()); } public static void assertNodeStartedEventually(final HazelcastInstance instance) { assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { assertNodeStarted(instance); } }); } // ################################ // ########## assertions ########## // ################################ public static void assertUtilityConstructor(Class clazz) { Constructor[] constructors = clazz.getDeclaredConstructors(); assertEquals("there are more than 1 constructors", 1, constructors.length); Constructor constructor = constructors[0]; int modifiers = constructor.getModifiers(); assertTrue("access modifier is not private", Modifier.isPrivate(modifiers)); constructor.setAccessible(true); try { constructor.newInstance(); } catch (Exception e) { ignore(e); } } public static void assertEnumCoverage(Class<? extends Enum<?>> enumClass) { Object values = null; Object lastValue = null; try { values = enumClass.getMethod("values").invoke(null); } catch (Throwable e) { fail("could not invoke values() method of enum " + enumClass); } try { for (Object value : (Object[]) values) { lastValue = value; enumClass.getMethod("valueOf", String.class).invoke(null, value.toString()); } } catch (Throwable e) { fail("could not invoke valueOf(" + lastValue + ") method of enum " + enumClass); } } public static <E> void assertContains(Collection<E> collection, E expected) { if (!collection.contains(expected)) { fail(format("Collection %s didn't contain expected '%s'", collection, expected)); } } public static <E> void assertNotContains(Collection<E> collection, E expected) { if (collection.contains(expected)) { fail(format("Collection %s contained unexpected '%s'", collection, expected)); } } public static <E> void assertContainsAll(Collection<E> collection, Collection<E> expected) { if (!collection.containsAll(expected)) { fail(format("Collection %s didn't contain expected %s", collection, expected)); } } public static <E> void assertNotContainsAll(Collection<E> collection, Collection<E> expected) { if (collection.containsAll(expected)) { fail(format("Collection %s contained unexpected %s", collection, expected)); } } public static void assertContains(String string, String expected) { if (!string.contains(expected)) { fail(format("'%s' didn't contain expected '%s'", string, expected)); } } public static void assertNotContains(String string, String expected) { if (string.contains(expected)) { fail(format("'%s' contained unexpected '%s'", string, expected)); } } public static void assertStartsWith(String expected, String actual) { if (actual != null && actual.startsWith(expected)) { return; } if (expected != null && actual != null) { throw new ComparisonFailure("", expected, actual); } fail(formatAssertMessage("", expected, null)); } private static String formatAssertMessage(String message, Object expected, Object actual) { StringBuilder assertMessage = new StringBuilder(); if (message != null && !message.isEmpty()) { assertMessage.append(message).append(" "); } String expectedString = String.valueOf(expected); String actualString = String.valueOf(actual); if (expectedString.equals(actualString)) { assertMessage.append("expected: "); formatClassAndValue(assertMessage, expected, expectedString); assertMessage.append(" but was: "); formatClassAndValue(assertMessage, actual, actualString); } else { assertMessage.append("expected: <").append(expectedString).append("> but was: <").append(actualString).append(">"); } return assertMessage.toString(); } private static void formatClassAndValue(StringBuilder message, Object value, String valueString) { message.append((value == null) ? "null" : value.getClass().getName()).append("<").append(valueString).append(">"); } @SuppressWarnings("unchecked") public static <E> E assertInstanceOf(Class<E> clazz, Object object) { assertNotNull(object); assertTrue(object + " is not an instanceof " + clazz.getName(), clazz.isAssignableFrom(object.getClass())); return (E) object; } public static void assertJoinable(Thread... threads) { assertJoinable(ASSERT_TRUE_EVENTUALLY_TIMEOUT, threads); } public static void assertJoinable(long timeoutSeconds, Thread... threads) { try { long remainingTimeout = TimeUnit.SECONDS.toNanos(timeoutSeconds); for (Thread thread : threads) { long start = System.nanoTime(); thread.join(remainingTimeout); if (thread.isAlive()) { fail("Timeout waiting for thread " + thread.getName() + " to terminate"); } long duration = System.nanoTime() - start; remainingTimeout -= duration; if (remainingTimeout <= 0) { fail("Timeout waiting for thread " + thread.getName() + " to terminate"); } } } catch (InterruptedException e) { throw new RuntimeException(e); } } public static void assertIterableEquals(Iterable iterable, Object... values) { List<Object> actual = new ArrayList<Object>(); for (Object object : iterable) { actual.add(object); } List expected = Arrays.asList(values); assertEquals("size should match", expected.size(), actual.size()); assertEquals(expected, actual); } public static void assertCompletesEventually(final Future future) { assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { assertTrue("Future has not completed", future.isDone()); } }); } public static void assertSizeEventually(int expectedSize, Collection collection) { assertSizeEventually(expectedSize, collection, ASSERT_TRUE_EVENTUALLY_TIMEOUT); } public static void assertSizeEventually(final int expectedSize, final Collection collection, long timeoutSeconds) { assertTrueEventually(new AssertTask() { @Override public void run() { assertEquals("the size of the collection is not correct: found-content:" + collection, expectedSize, collection.size()); } }, timeoutSeconds); } public static void assertSizeEventually(int expectedSize, Map<?, ?> map) { assertSizeEventually(expectedSize, map, ASSERT_TRUE_EVENTUALLY_TIMEOUT); } public static void assertSizeEventually(final int expectedSize, final Map<?, ?> map, long timeoutSeconds) { assertTrueEventually(new AssertTask() { @Override public void run() { assertEquals("the size of the map is not correct", expectedSize, map.size()); } }, timeoutSeconds); } public static <E> void assertEqualsEventually(final FutureTask<E> task, final E value) { assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { assertTrue("FutureTask is not complete", task.isDone()); assertEquals(value, task.get()); } }); } public static <E> void assertEqualsEventually(final Callable<E> task, final E value) { assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { assertEquals(value, task.call()); } }); } public static void assertClusterSize(int expectedSize, HazelcastInstance... instances) { for (HazelcastInstance instance : instances) { int clusterSize = getClusterSize(instance); if (expectedSize != clusterSize) { fail(format("Cluster size is not correct. Expected: %d Actual: %d", expectedSize, clusterSize)); } } } private static int getClusterSize(HazelcastInstance instance) { Set<Member> members = instance.getCluster().getMembers(); return members == null ? 0 : members.size(); } public static void assertClusterSizeEventually(int expectedSize, HazelcastInstance... instances) { for (HazelcastInstance instance : instances) { assertClusterSizeEventually(expectedSize, instance, ASSERT_TRUE_EVENTUALLY_TIMEOUT); } } public static void assertClusterSizeEventually(final int expectedSize, final HazelcastInstance instance, long timeoutSeconds) { assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { assertClusterSize(expectedSize, instance); } }, timeoutSeconds); } public static void assertMasterAddress(Address masterAddress, HazelcastInstance... instances) { for (HazelcastInstance instance : instances) { assertEquals(masterAddress, getNode(instance).getMasterAddress()); } } public static void assertMasterAddressEventually(final Address masterAddress, final HazelcastInstance... instances) { assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { for (HazelcastInstance instance : instances) { assertMasterAddress(masterAddress, instance); } } }); } public static void assertClusterState(ClusterState expectedState, HazelcastInstance... instances) { for (HazelcastInstance instance : instances) { assertEquals("Instance " + instance.getCluster().getLocalMember(), expectedState, instance.getCluster().getClusterState()); } } public static void assertClusterStateEventually(final ClusterState expectedState, final HazelcastInstance... instances) { assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { assertClusterState(expectedState, instances); } }); } public static void assertOpenEventually(CountDownLatch latch) { assertOpenEventually(latch, ASSERT_TRUE_EVENTUALLY_TIMEOUT); } public static void assertOpenEventually(String message, CountDownLatch latch) { assertOpenEventually(message, latch, ASSERT_TRUE_EVENTUALLY_TIMEOUT); } public static void assertOpenEventually(CountDownLatch latch, long timeoutSeconds) { assertOpenEventually(null, latch, timeoutSeconds); } public static void assertOpenEventually(String message, CountDownLatch latch, long timeoutSeconds) { try { boolean completed = latch.await(timeoutSeconds, TimeUnit.SECONDS); if (message == null) { assertTrue(format("CountDownLatch failed to complete within %d seconds, count left: %d", timeoutSeconds, latch.getCount()), completed); } else { assertTrue(format("%s, failed to complete within %d seconds, count left: %d", message, timeoutSeconds, latch.getCount()), completed); } } catch (InterruptedException e) { throw new RuntimeException(e); } } public static void assertCountEventually(final String message, final int expectedCount, final CountDownLatch latch, long timeoutInSeconds) { assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { for (int i = 0; i < 2; i++) { // recheck to see if hasn't changed if (latch.getCount() != expectedCount) { throw new AssertionError("Latch count has not been met. " + message); } sleepMillis(50); } } }, timeoutInSeconds); } public static void assertAtomicEventually(final String message, final int expectedValue, final AtomicInteger atomic, int timeoutInSeconds) { assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { for (int i = 0; i < 2; i++) { // recheck to see if hasn't changed if (atomic.get() != expectedValue) { throw new AssertionError("Atomic value has not been met. " + message); } sleepMillis(50); } } }, timeoutInSeconds); } public static void assertAtomicEventually(final String message, final boolean expectedValue, final AtomicBoolean atomic, int timeoutInSeconds) { assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { for (int i = 0; i < 2; i++) { // recheck to see if hasn't changed if (atomic.get() != expectedValue) { throw new AssertionError("Atomic value has not been met. " + message); } sleepMillis(50); } } }, timeoutInSeconds); } public static void assertFieldEqualsTo(Object object, String fieldName, Object expectedValue) { Class<?> clazz = object.getClass(); try { Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); Object actualValue = field.get(object); assertEquals(expectedValue, actualValue); } catch (NoSuchFieldException e) { fail("Class " + clazz + " does not have field named " + fieldName + " declared"); } catch (IllegalAccessException e) { fail("Cannot access field " + fieldName + " on class " + clazz); } } public static void assertTrueFiveSeconds(AssertTask task) { assertTrueAllTheTime(task, 5); } public static void assertTrueAllTheTime(AssertTask task, long durationSeconds) { for (int i = 0; i < durationSeconds; i++) { try { task.run(); } catch (Exception e) { throw rethrow(e); } sleepSeconds(1); } } public static void assertTrueEventually(AssertTask task, long timeoutSeconds) { AssertionError error = null; // we are going to check five times a second int sleepMillis = 200; long iterations = timeoutSeconds * 5; for (int i = 0; i < iterations; i++) { try { try { task.run(); } catch (Exception e) { throw rethrow(e); } return; } catch (AssertionError e) { error = e; } sleepMillis(sleepMillis); } if (error != null) { throw error; } fail("assertTrueEventually() failed without AssertionError!"); } public static void assertTrueEventually(AssertTask task) { assertTrueEventually(task, ASSERT_TRUE_EVENTUALLY_TIMEOUT); } public static void assertTrueDelayed5sec(AssertTask task) { assertTrueDelayed(5, task); } public static void assertTrueDelayed(int delaySeconds, AssertTask task) { sleepSeconds(delaySeconds); try { task.run(); } catch (Exception e) { throw rethrow(e); } } /** * This method executes the normal assertEquals with expected and actual values. * In addition it formats the given string with those values to provide a good assert message. * * @param message assert message which is formatted with expected and actual values * @param expected expected value which is used for assert * @param actual actual value which is used for assert */ public static void assertEqualsStringFormat(String message, Object expected, Object actual) { assertEquals(format(message, expected, actual), expected, actual); } public static void assertExactlyOneSuccessfulRun(AssertTask task) { assertExactlyOneSuccessfulRun(task, ASSERT_TRUE_EVENTUALLY_TIMEOUT, TimeUnit.SECONDS); } public static void assertExactlyOneSuccessfulRun(AssertTask task, int giveUpTime, TimeUnit timeUnit) { long timeout = System.currentTimeMillis() + timeUnit.toMillis(giveUpTime); RuntimeException lastException = new RuntimeException("Did not try even once"); while (System.currentTimeMillis() < timeout) { try { task.run(); return; } catch (Exception e) { lastException = e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e); } try { Thread.sleep(250); } catch (InterruptedException e) { lastException = new RuntimeException(e); } } throw lastException; } public static void assertWaitingOperationCountEventually(int expectedOpsCount, HazelcastInstance... instances) { for (HazelcastInstance instance : instances) { assertWaitingOperationCountEventually(expectedOpsCount, instance); } } public static void assertWaitingOperationCountEventually(final int expectedOpsCount, HazelcastInstance instance) { final OperationParkerImpl waitNotifyService = getOperationParkingService(instance); assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { assertEquals(expectedOpsCount, waitNotifyService.getTotalParkedOperationCount()); } }); } private static OperationParkerImpl getOperationParkingService(HazelcastInstance instance) { return (OperationParkerImpl) getNodeEngineImpl(instance).getOperationParker(); } // ################################### // ########## reflection utils ####### // ################################### public static Object getFromField(Object target, String fieldName) { try { Field field = target.getClass().getDeclaredField(fieldName); if (!Modifier.isPublic(field.getModifiers())) { field.setAccessible(true); } return field.get(target); } catch (NoSuchFieldException e) { throw new AssertionError(e); } catch (IllegalAccessException e) { throw new AssertionError(e); } } // ################################### // ########## inner classes ########## // ################################### public static final class DummyUncheckedHazelcastTestException extends RuntimeException { } public static class DummySerializableCallable implements Callable, Serializable { @Override public Object call() throws Exception { return null; } } // ############################################ // ########## read from backup utils ########## // ############################################ protected Object readFromMapBackup(HazelcastInstance instance, String mapName, Object key) { return readFromMapBackup(instance, mapName, key, 1); } protected Object readFromMapBackup(HazelcastInstance instance, String mapName, Object key, int replicaIndex) { try { NodeEngine nodeEngine = getNode(instance).getNodeEngine(); SerializationService ss = getNode(instance).getSerializationService(); int partitionId = nodeEngine.getPartitionService().getPartitionId(key); MapOperation get = getMapOperationProvider().createGetOperation(mapName, ss.toData(key)); get.setPartitionId(partitionId); get.setReplicaIndex(replicaIndex); return getNode(instance).getNodeEngine().getOperationService().invokeOnPartition(get).get(); } catch (Exception ex) { throw new RuntimeException(ex); } } protected MapOperationProvider getMapOperationProvider() { return new DefaultMapOperationProvider(); } }