package org.infinispan.counter; import static java.lang.String.format; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.ExecutionException; import org.infinispan.counter.api.CounterManager; import org.infinispan.counter.exception.CounterOutOfBoundsException; import org.infinispan.counter.impl.BaseCounterTest; import org.infinispan.counter.util.TestCounter; import org.testng.annotations.Test; /** * @author Pedro Ruivo * @since 9.0 */ @Test(groups = "functional") public abstract class AbstractCounterTest<T extends TestCounter> extends BaseCounterTest { public void testDiffInitValues(Method method) throws ExecutionException, InterruptedException { final TestContext context = new TestContext(); final String counterName = method.getName(); long initialValue = 0; context.printSeed(counterName); List<T> counters = new ArrayList<>(clusterSize()); for (int i = 0; i < clusterSize(); ++i) { long rndLong = context.random.nextLong(); if (i == 0) { initialValue = rndLong; } log.debug(context.message("StrongCounter #%d initial value is %d", i, rndLong)); counters.add(createCounter(counterManager(i), counterName, rndLong)); } for (int i = 0; i < clusterSize(); ++i) { final int index = i; eventuallyEquals(context.message("Wrong initial value for counter #%d", i), initialValue, () -> counters.get(index).getValue()); } } public void testReset(Method method) throws ExecutionException, InterruptedException { final String counterName = method.getName(); final TestContext context = new TestContext(); context.printSeed(counterName); final long initialValue = context.random.nextLong(); List<T> counters = new ArrayList<>(clusterSize()); for (int i = 0; i < clusterSize(); ++i) { counters.add(createCounter(counterManager(i), counterName, initialValue)); } for (int i = 0; i < clusterSize(); ++i) { long delta = context.random.nextLong(); addIgnoringBounds(counters.get(i), delta); log.debug(context.message("StrongCounter #%d, Add %d", i, delta)); counters.get(i).reset(); final int index = i; eventuallyEquals(context.message("Wrong initial value for counter #%d", i), initialValue, () -> counters.get(index).getValue()); } for (int i = 0; i < clusterSize(); ++i) { long delta = context.random.nextLong(); addIgnoringBounds(counters.get(i), delta); log.debug(context.message("StrongCounter #%d, Add %d", i, delta)); counters.get(0).reset(); final int index = i; eventuallyEquals(context.message("Wrong initial value for counter #%d", i), initialValue, () -> counters.get(index).getValue()); } } public void testMaxAndMinLong(Method method) throws ExecutionException, InterruptedException { final String counterName = method.getName(); T counter = createCounter(counterManager(0), counterName, 0); addAndAssertResult(counter, Long.MAX_VALUE - 1, Long.MAX_VALUE - 1); addAndAssertResult(counter, 1, Long.MAX_VALUE); assertMaxValueAfterMaxValue(counter, 1); assertMaxValueAfterMaxValue(counter, 1000); addAndAssertResult(counter, -1, Long.MAX_VALUE - 1); counter.reset(); addAndAssertResult(counter, Long.MIN_VALUE + 1, Long.MIN_VALUE + 1); addAndAssertResult(counter, -1, Long.MIN_VALUE); assertMinValueAfterMinValue(counter, -1); assertMinValueAfterMinValue(counter, -1000); addAndAssertResult(counter, 1, Long.MIN_VALUE + 1); } protected abstract void assertMinValueAfterMinValue(T counter, long delta); protected abstract T createCounter(CounterManager counterManager, String counterName, long initialValue); protected abstract void assertMaxValueAfterMaxValue(T counter, long delta); protected abstract void addAndAssertResult(T counter, long delta, long expected); private void addIgnoringBounds(T counter, long delta) { try { counter.add(delta); } catch (CounterOutOfBoundsException e) { log.debug("ignored.", e); } } class TestContext { final Random random; private final long seed; TestContext() { this(System.nanoTime()); } //in case we need to test with specific seed private TestContext(long seed) { this.seed = seed; this.random = new Random(seed); } void printSeed(String testName) { log.infof("Test '%s' seed is %d", testName, seed); } String message(String format, Object... args) { return "[seed=" + seed + "] " + format(format, args); } } }