Repository: incubator-ratis Updated Branches: refs/heads/master d28b6493f -> 5c37675fa
http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/statemachine/TestStateMachine.java ---------------------------------------------------------------------- diff --git a/ratis-test/src/test/java/org/apache/ratis/statemachine/TestStateMachine.java b/ratis-test/src/test/java/org/apache/ratis/statemachine/TestStateMachine.java new file mode 100644 index 0000000..a4dc88a --- /dev/null +++ b/ratis-test/src/test/java/org/apache/ratis/statemachine/TestStateMachine.java @@ -0,0 +1,201 @@ +/** + * 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.ratis.statemachine; + +import org.apache.log4j.Level; +import org.apache.ratis.BaseTest; +import org.apache.ratis.MiniRaftCluster; +import org.apache.ratis.RaftTestUtil; +import org.apache.ratis.client.RaftClient; +import org.apache.ratis.conf.RaftProperties; +import org.apache.ratis.protocol.Message; +import org.apache.ratis.protocol.RaftClientRequest; +import org.apache.ratis.protocol.RaftGroup; +import org.apache.ratis.protocol.RaftGroupId; +import org.apache.ratis.protocol.RaftPeer; +import org.apache.ratis.protocol.RaftPeerId; +import org.apache.ratis.server.RaftServerConfigKeys; +import org.apache.ratis.server.impl.RaftServerImpl; +import org.apache.ratis.server.impl.RaftServerProxy; +import org.apache.ratis.server.impl.RaftServerTestUtil; +import org.apache.ratis.server.simulation.MiniRaftClusterWithSimulatedRpc; +import org.apache.ratis.util.LogUtils; +import org.junit.*; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import static org.junit.Assert.*; + +/** + * Test StateMachine related functionality + */ +public class TestStateMachine extends BaseTest implements MiniRaftClusterWithSimulatedRpc.FactoryGet { + static { + LogUtils.setLogLevel(RaftServerImpl.LOG, Level.DEBUG); + LogUtils.setLogLevel(RaftClient.LOG, Level.DEBUG); + } + + public static final int NUM_SERVERS = 3; + + static class SMTransactionContext extends SimpleStateMachine4Testing { + public static SMTransactionContext get(RaftServerImpl s) { + return (SMTransactionContext)s.getStateMachine(); + } + + AtomicReference<Throwable> throwable = new AtomicReference<>(null); + AtomicLong transactions = new AtomicLong(0); + AtomicBoolean isLeader = new AtomicBoolean(false); + AtomicLong numApplied = new AtomicLong(0); + ConcurrentLinkedQueue<Long> applied = new ConcurrentLinkedQueue<>(); + + @Override + public TransactionContext startTransaction(RaftClientRequest request) { + // only leader will get this call + isLeader.set(true); + // send the next transaction id as the "context" from SM + return TransactionContext.newBuilder() + .setStateMachine(this) + .setClientRequest(request) + .setStateMachineContext(transactions.incrementAndGet()) + .build(); + } + + @Override + public CompletableFuture<Message> applyTransaction(TransactionContext trx) { + try { + assertNotNull(trx.getLogEntry()); + assertNotNull(trx.getStateMachineLogEntry()); + Object context = trx.getStateMachineContext(); + if (isLeader.get()) { + assertNotNull(trx.getClientRequest()); + assertNotNull(context); + assertTrue(context instanceof Long); + Long val = (Long)context; + assertTrue(val <= transactions.get()); + applied.add(val); + } else { + assertNull(trx.getClientRequest()); + assertNull(context); + } + numApplied.incrementAndGet(); + } catch (Throwable t) { + throwable.set(t); + } + return CompletableFuture.completedFuture(null); + } + + void rethrowIfException() throws Throwable { + Throwable t = throwable.get(); + if (t != null) { + throw t; + } + } + } + + @Test + public void testTransactionContextIsPassedBack() throws Throwable { + runTestTransactionContextIsPassedBack(false); + } + + @Test + public void testTransactionContextIsPassedBackUseMemory() throws Throwable { + runTestTransactionContextIsPassedBack(true); + } + + void runTestTransactionContextIsPassedBack(boolean useMemory) throws Throwable { + final RaftProperties properties = new RaftProperties(); + properties.setClass(MiniRaftCluster.STATEMACHINE_CLASS_KEY, SMTransactionContext.class, StateMachine.class); + RaftServerConfigKeys.Log.setUseMemory(properties, useMemory); + + try(MiniRaftClusterWithSimulatedRpc cluster = getFactory().newCluster(NUM_SERVERS, properties)) { + cluster.start(); + runTestTransactionContextIsPassedBack(cluster); + } + } + + static void runTestTransactionContextIsPassedBack(MiniRaftCluster cluster) throws Throwable { + // tests that the TrxContext set by the StateMachine in Leader is passed back to the SM + int numTrx = 100; + final RaftTestUtil.SimpleMessage[] messages = RaftTestUtil.SimpleMessage.create(numTrx); + try(final RaftClient client = cluster.createClient()) { + for (RaftTestUtil.SimpleMessage message : messages) { + client.send(message); + } + } + + // TODO: there eshould be a better way to ensure all data is replicated and applied + Thread.sleep(cluster.getMaxTimeout() + 100); + + for (RaftServerImpl raftServer : cluster.iterateServerImpls()) { + final SMTransactionContext sm = SMTransactionContext.get(raftServer); + sm.rethrowIfException(); + assertEquals(numTrx, sm.numApplied.get()); + } + + // check leader + RaftServerImpl raftServer = cluster.getLeader(); + // assert every transaction has obtained context in leader + final SMTransactionContext sm = SMTransactionContext.get(raftServer); + List<Long> ll = sm.applied.stream().collect(Collectors.toList()); + Collections.sort(ll); + assertEquals(ll.toString(), ll.size(), numTrx); + for (int i=0; i < numTrx; i++) { + assertEquals(ll.toString(), Long.valueOf(i+1), ll.get(i)); + } + } + + @Test + public void testStateMachineRegistry() throws Throwable { + final Map<RaftGroupId, StateMachine> registry = new ConcurrentHashMap<>(); + registry.put(RaftGroupId.randomId(), new SimpleStateMachine4Testing()); + registry.put(RaftGroupId.randomId(), new SMTransactionContext()); + + try(MiniRaftClusterWithSimulatedRpc cluster = newCluster(0)) { + cluster.setStateMachineRegistry(registry::get); + + final RaftPeerId id = RaftPeerId.valueOf("s0"); + cluster.putNewServer(id, null, true); + cluster.start(); + + for(RaftGroupId gid : registry.keySet()) { + final RaftGroup newGroup = RaftGroup.valueOf(gid, cluster.getPeers()); + LOG.info("add new group: " + newGroup); + final RaftClient client = cluster.createClient(newGroup); + for(RaftPeer p : newGroup.getPeers()) { + client.groupAdd(newGroup, p.getId()); + } + } + + final RaftServerProxy proxy = cluster.getServer(id); + for(Map.Entry<RaftGroupId, StateMachine> e: registry.entrySet()) { + final RaftServerImpl impl = RaftServerTestUtil.getRaftServerImpl(proxy, e.getKey()); + Assert.assertSame(e.getValue(), impl.getStateMachine()); + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/util/TestLifeCycle.java ---------------------------------------------------------------------- diff --git a/ratis-test/src/test/java/org/apache/ratis/util/TestLifeCycle.java b/ratis-test/src/test/java/org/apache/ratis/util/TestLifeCycle.java new file mode 100644 index 0000000..9782792 --- /dev/null +++ b/ratis-test/src/test/java/org/apache/ratis/util/TestLifeCycle.java @@ -0,0 +1,53 @@ +/** + * 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.ratis.util; + +import org.junit.Assert; +import org.junit.Test; + +import static org.apache.ratis.util.LifeCycle.State.*; + +import java.util.*; + +public class TestLifeCycle { + /** + * Test if the successor map and the predecessor map are consistent. + * {@link LifeCycle} uses predecessors to validate transitions + * while this test uses successors. + */ + @Test(timeout = 1000) + public void testIsValid() throws Exception { + final Map<LifeCycle.State, List<LifeCycle.State>> successors + = new EnumMap<>(LifeCycle.State.class); + put(NEW, successors, STARTING, CLOSED); + put(STARTING, successors, NEW, RUNNING, CLOSING, EXCEPTION); + put(RUNNING, successors, CLOSING, PAUSING, EXCEPTION); + put(PAUSING, successors, PAUSED, CLOSING, EXCEPTION); + put(PAUSED, successors, STARTING, CLOSING); + put(EXCEPTION, successors, CLOSING); + put(CLOSING , successors, CLOSED); + put(CLOSED, successors); + + final List<LifeCycle.State> states = Arrays.asList(LifeCycle.State.values()); + states.stream().forEach( + from -> states.forEach( + to -> Assert.assertEquals(from + " -> " + to, + successors.get(from).contains(to), + isValid(from, to)))); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/util/TestMinMax.java ---------------------------------------------------------------------- diff --git a/ratis-test/src/test/java/org/apache/ratis/util/TestMinMax.java b/ratis-test/src/test/java/org/apache/ratis/util/TestMinMax.java new file mode 100644 index 0000000..8d315b7 --- /dev/null +++ b/ratis-test/src/test/java/org/apache/ratis/util/TestMinMax.java @@ -0,0 +1,57 @@ +/** + * 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.ratis.util; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.OptionalLong; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.LongStream; + +public class TestMinMax { + @Test(timeout = 1000) + public void testMinMax() { + runTestMinMax(LongStream.empty()); + runTestMinMax(LongStream.iterate(0, n -> n).limit(10)); + for(int count = 1; count < 10; count++) { + runTestMinMax(LongStream.iterate(1, n -> n + 1).limit(count)); + } + for(int count = 1; count < 10; count++) { + runTestMinMax(LongStream.iterate(0, _dummy -> ThreadLocalRandom.current().nextLong()).limit(count)); + } + } + + static void runTestMinMax(LongStream stream) { + final List<Long> list = stream.collect(ArrayList::new, List::add, List::addAll); + final LongMinMax longMinMax = toLongStream(list).collect(LongMinMax::new, LongMinMax::accumulate, LongMinMax::combine); + if (longMinMax.isInitialized()) { + Assert.assertEquals(toLongStream(list).min().getAsLong(), longMinMax.getMin()); + Assert.assertEquals(toLongStream(list).max().getAsLong(), longMinMax.getMax()); + } else { + Assert.assertEquals(OptionalLong.empty(), toLongStream(list).min()); + Assert.assertEquals(OptionalLong.empty(), toLongStream(list).max()); + } + } + + static LongStream toLongStream(List<Long> list) { + return list.stream().mapToLong(Long::longValue); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/util/TestTimeDuration.java ---------------------------------------------------------------------- diff --git a/ratis-test/src/test/java/org/apache/ratis/util/TestTimeDuration.java b/ratis-test/src/test/java/org/apache/ratis/util/TestTimeDuration.java new file mode 100644 index 0000000..06d9301 --- /dev/null +++ b/ratis-test/src/test/java/org/apache/ratis/util/TestTimeDuration.java @@ -0,0 +1,84 @@ +/** + * 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.ratis.util; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static org.apache.ratis.util.TimeDuration.Abbreviation; +import static org.apache.ratis.util.TimeDuration.parse; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class TestTimeDuration { + @Test(timeout = 1000) + public void testTimeDuration() throws Exception { + Arrays.asList(TimeUnit.values()) + .forEach(a -> assertNotNull(Abbreviation.valueOf(a.name()))); + assertEquals(TimeUnit.values().length, Abbreviation.values().length); + + final List<String> allSymbols = Arrays.asList(Abbreviation.values()).stream() + .map(Abbreviation::getSymbols) + .flatMap(List::stream) + .collect(Collectors.toList()); + Arrays.asList(TimeUnit.values()).forEach(unit -> + allSymbols.stream() + .map(s -> "0" + s) + .forEach(s -> assertEquals(s, 0L, parse(s, unit)))); + + assertEquals(1L, parse("1000000 ns", TimeUnit.MILLISECONDS)); + assertEquals(10L, parse("10000000 nanos", TimeUnit.MILLISECONDS)); + assertEquals(100L, parse("100000000 nanosecond", TimeUnit.MILLISECONDS)); + assertEquals(1000L, parse("1000000000 nanoseconds", TimeUnit.MILLISECONDS)); + + assertEquals(1L, parse("1000 us", TimeUnit.MILLISECONDS)); + assertEquals(10L, parse("10000 μs", TimeUnit.MILLISECONDS)); + assertEquals(100L, parse("100000 micros", TimeUnit.MILLISECONDS)); + assertEquals(1000L, parse("1000000 microsecond", TimeUnit.MILLISECONDS)); + assertEquals(10000L, parse("10000000 microseconds", TimeUnit.MILLISECONDS)); + + assertEquals(1L, parse("1 ms", TimeUnit.MILLISECONDS)); + assertEquals(10L, parse("10 msec", TimeUnit.MILLISECONDS)); + assertEquals(100L, parse("100 millis", TimeUnit.MILLISECONDS)); + assertEquals(1000L, parse("1000 millisecond", TimeUnit.MILLISECONDS)); + assertEquals(10000L, parse("10000 milliseconds", TimeUnit.MILLISECONDS)); + + assertEquals(1000L, parse("1 s", TimeUnit.MILLISECONDS)); + assertEquals(10000L, parse("10 sec", TimeUnit.MILLISECONDS)); + assertEquals(100000L, parse("100 second", TimeUnit.MILLISECONDS)); + assertEquals(1000000L, parse("1000 seconds", TimeUnit.MILLISECONDS)); + + assertEquals(60, parse("1 m", TimeUnit.SECONDS)); + assertEquals(600, parse("10 min", TimeUnit.SECONDS)); + assertEquals(6000, parse("100 minutes", TimeUnit.SECONDS)); + assertEquals(60000, parse("1000 minutes", TimeUnit.SECONDS)); + + assertEquals(60, parse("1 h", TimeUnit.MINUTES)); + assertEquals(600, parse("10 hr", TimeUnit.MINUTES)); + assertEquals(6000, parse("100 hour", TimeUnit.MINUTES)); + assertEquals(60000, parse("1000 hours", TimeUnit.MINUTES)); + + assertEquals(24, parse("1 d", TimeUnit.HOURS)); + assertEquals(240, parse("10 day", TimeUnit.HOURS)); + assertEquals(2400, parse("100 days", TimeUnit.HOURS)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/util/TestTimeoutScheduler.java ---------------------------------------------------------------------- diff --git a/ratis-test/src/test/java/org/apache/ratis/util/TestTimeoutScheduler.java b/ratis-test/src/test/java/org/apache/ratis/util/TestTimeoutScheduler.java new file mode 100644 index 0000000..6a63569 --- /dev/null +++ b/ratis-test/src/test/java/org/apache/ratis/util/TestTimeoutScheduler.java @@ -0,0 +1,210 @@ +/** + * 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.ratis.util; + +import org.apache.log4j.Level; +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; + +public class TestTimeoutScheduler { + { + LogUtils.setLogLevel(TimeoutScheduler.LOG, Level.ALL); + } + + static class ErrorHandler implements Consumer<RuntimeException> { + private final AtomicBoolean hasError = new AtomicBoolean(false); + + @Override + public void accept(RuntimeException e) { + hasError.set(true); + TimeoutScheduler.LOG.error("Failed", e); + } + + void assertNoError() { + Assert.assertFalse(hasError.get()); + } + } + + @Test(timeout = 1000) + public void testSingleTask() throws Exception { + final TimeoutScheduler scheduler = TimeoutScheduler.newInstance(1); + final TimeDuration grace = TimeDuration.valueOf(100, TimeUnit.MILLISECONDS); + scheduler.setGracePeriod(grace); + Assert.assertFalse(scheduler.hasScheduler()); + + final ErrorHandler errorHandler = new ErrorHandler(); + + final AtomicBoolean fired = new AtomicBoolean(false); + scheduler.onTimeout(TimeDuration.valueOf(250, TimeUnit.MILLISECONDS), () -> { + Assert.assertFalse(fired.get()); + fired.set(true); + }, errorHandler); + Assert.assertTrue(scheduler.hasScheduler()); + + Thread.sleep(100); + Assert.assertFalse(fired.get()); + Assert.assertTrue(scheduler.hasScheduler()); + + Thread.sleep(100); + Assert.assertFalse(fired.get()); + Assert.assertTrue(scheduler.hasScheduler()); + + Thread.sleep(100); + Assert.assertTrue(fired.get()); + Assert.assertTrue(scheduler.hasScheduler()); + + Thread.sleep(100); + Assert.assertTrue(fired.get()); + Assert.assertFalse(scheduler.hasScheduler()); + + errorHandler.assertNoError(); + } + + @Test(timeout = 1000) + public void testMultipleTasks() throws Exception { + final TimeoutScheduler scheduler = TimeoutScheduler.newInstance(1); + final TimeDuration grace = TimeDuration.valueOf(100, TimeUnit.MILLISECONDS); + scheduler.setGracePeriod(grace); + Assert.assertFalse(scheduler.hasScheduler()); + + final ErrorHandler errorHandler = new ErrorHandler(); + + final AtomicBoolean[] fired = new AtomicBoolean[3]; + for(int i = 0; i < fired.length; i++) { + final AtomicBoolean f = fired[i] = new AtomicBoolean(false); + scheduler.onTimeout(TimeDuration.valueOf(100*i + 50, TimeUnit.MILLISECONDS), () -> { + Assert.assertFalse(f.get()); + f.set(true); + }, errorHandler); + Assert.assertTrue(scheduler.hasScheduler()); + } + + Thread.sleep(100); + Assert.assertTrue(fired[0].get()); + Assert.assertFalse(fired[1].get()); + Assert.assertFalse(fired[2].get()); + Assert.assertTrue(scheduler.hasScheduler()); + + Thread.sleep(100); + Assert.assertTrue(fired[0].get()); + Assert.assertTrue(fired[1].get()); + Assert.assertFalse(fired[2].get()); + Assert.assertTrue(scheduler.hasScheduler()); + + Thread.sleep(100); + Assert.assertTrue(fired[0].get()); + Assert.assertTrue(fired[1].get()); + Assert.assertTrue(fired[2].get()); + Assert.assertTrue(scheduler.hasScheduler()); + + Thread.sleep(100); + Assert.assertTrue(fired[0].get()); + Assert.assertTrue(fired[1].get()); + Assert.assertTrue(fired[2].get()); + Assert.assertFalse(scheduler.hasScheduler()); + + errorHandler.assertNoError(); + } + + @Test(timeout = 1000) + public void testExtendingGracePeriod() throws Exception { + final TimeoutScheduler scheduler = TimeoutScheduler.newInstance(1); + final TimeDuration grace = TimeDuration.valueOf(100, TimeUnit.MILLISECONDS); + scheduler.setGracePeriod(grace); + Assert.assertFalse(scheduler.hasScheduler()); + + final ErrorHandler errorHandler = new ErrorHandler(); + + { + final AtomicBoolean fired = new AtomicBoolean(false); + scheduler.onTimeout(TimeDuration.valueOf(150, TimeUnit.MILLISECONDS), () -> { + Assert.assertFalse(fired.get()); + fired.set(true); + }, errorHandler); + Assert.assertTrue(scheduler.hasScheduler()); + + Thread.sleep(100); + Assert.assertFalse(fired.get()); + Assert.assertTrue(scheduler.hasScheduler()); + + Thread.sleep(100); + Assert.assertTrue(fired.get()); + Assert.assertTrue(scheduler.hasScheduler()); + } + + { + // submit another task during grace period + final AtomicBoolean fired2 = new AtomicBoolean(false); + scheduler.onTimeout(TimeDuration.valueOf(150, TimeUnit.MILLISECONDS), () -> { + Assert.assertFalse(fired2.get()); + fired2.set(true); + }, errorHandler); + + Thread.sleep(100); + Assert.assertFalse(fired2.get()); + Assert.assertTrue(scheduler.hasScheduler()); + + Thread.sleep(100); + Assert.assertTrue(fired2.get()); + Assert.assertTrue(scheduler.hasScheduler()); + + Thread.sleep(100); + Assert.assertTrue(fired2.get()); + Assert.assertFalse(scheduler.hasScheduler()); + } + + errorHandler.assertNoError(); + } + + @Test(timeout = 1000) + public void testRestartingScheduler() throws Exception { + final TimeoutScheduler scheduler = TimeoutScheduler.newInstance(1); + final TimeDuration grace = TimeDuration.valueOf(100, TimeUnit.MILLISECONDS); + scheduler.setGracePeriod(grace); + Assert.assertFalse(scheduler.hasScheduler()); + + final ErrorHandler errorHandler = new ErrorHandler(); + + for(int i = 0; i < 2; i++) { + final AtomicBoolean fired = new AtomicBoolean(false); + scheduler.onTimeout(TimeDuration.valueOf(150, TimeUnit.MILLISECONDS), () -> { + Assert.assertFalse(fired.get()); + fired.set(true); + }, errorHandler); + Assert.assertTrue(scheduler.hasScheduler()); + + Thread.sleep(100); + Assert.assertFalse(fired.get()); + Assert.assertTrue(scheduler.hasScheduler()); + + Thread.sleep(100); + Assert.assertTrue(fired.get()); + Assert.assertTrue(scheduler.hasScheduler()); + + Thread.sleep(100); + Assert.assertTrue(fired.get()); + Assert.assertFalse(scheduler.hasScheduler()); + } + + errorHandler.assertNoError(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/java/org/apache/ratis/util/TestTraditionalBinaryPrefix.java ---------------------------------------------------------------------- diff --git a/ratis-test/src/test/java/org/apache/ratis/util/TestTraditionalBinaryPrefix.java b/ratis-test/src/test/java/org/apache/ratis/util/TestTraditionalBinaryPrefix.java new file mode 100644 index 0000000..26a62da --- /dev/null +++ b/ratis-test/src/test/java/org/apache/ratis/util/TestTraditionalBinaryPrefix.java @@ -0,0 +1,145 @@ +/** + * 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.ratis.util; + +import org.junit.Test; + +import static org.apache.ratis.util.TraditionalBinaryPrefix.long2String; +import static org.apache.ratis.util.TraditionalBinaryPrefix.string2long; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class TestTraditionalBinaryPrefix { + @Test(timeout = 1000) + public void testTraditionalBinaryPrefix() throws Exception { + //test string2long(..) + String[] symbol = {"k", "m", "g", "t", "p", "e"}; + long m = 1024; + for(String s : symbol) { + assertEquals(0, string2long(0 + s)); + assertEquals(m, string2long(1 + s)); + m *= 1024; + } + + assertEquals(0L, string2long("0")); + assertEquals(1024L, string2long("1k")); + assertEquals(-1024L, string2long("-1k")); + assertEquals(1259520L, string2long("1230K")); + assertEquals(-1259520L, string2long("-1230K")); + assertEquals(104857600L, string2long("100m")); + assertEquals(-104857600L, string2long("-100M")); + assertEquals(956703965184L, string2long("891g")); + assertEquals(-956703965184L, string2long("-891G")); + assertEquals(501377302265856L, string2long("456t")); + assertEquals(-501377302265856L, string2long("-456T")); + assertEquals(11258999068426240L, string2long("10p")); + assertEquals(-11258999068426240L, string2long("-10P")); + assertEquals(1152921504606846976L, string2long("1e")); + assertEquals(-1152921504606846976L, string2long("-1E")); + + String tooLargeNumStr = "10e"; + try { + string2long(tooLargeNumStr); + fail("Test passed for a number " + tooLargeNumStr + " too large"); + } catch (IllegalArgumentException e) { + assertEquals(tooLargeNumStr + " does not fit in a Long", e.getMessage()); + } + + String tooSmallNumStr = "-10e"; + try { + string2long(tooSmallNumStr); + fail("Test passed for a number " + tooSmallNumStr + " too small"); + } catch (IllegalArgumentException e) { + assertEquals(tooSmallNumStr + " does not fit in a Long", e.getMessage()); + } + + String invalidFormatNumStr = "10kb"; + char invalidPrefix = 'b'; + try { + string2long(invalidFormatNumStr); + fail("Test passed for a number " + invalidFormatNumStr + + " has invalid format"); + } catch (IllegalArgumentException e) { + assertEquals("Invalid size prefix '" + invalidPrefix + "' in '" + + invalidFormatNumStr + + "'. Allowed prefixes are k, m, g, t, p, e (case insensitive)", + e.getMessage()); + } + + //test long2string(..) + assertEquals("0", long2String(0, null, 2)); + for(int decimalPlace = 0; decimalPlace < 2; decimalPlace++) { + for(int n = 1; n < TraditionalBinaryPrefix.KILO.getValue(); n++) { + assertEquals(n + "", long2String(n, null, decimalPlace)); + assertEquals(-n + "", long2String(-n, null, decimalPlace)); + } + assertEquals("1 K", long2String(1L << 10, null, decimalPlace)); + assertEquals("-1 K", long2String(-1L << 10, null, decimalPlace)); + } + + assertEquals("8.00 E", long2String(Long.MAX_VALUE, null, 2)); + assertEquals("8.00 E", long2String(Long.MAX_VALUE - 1, null, 2)); + assertEquals("-8 E", long2String(Long.MIN_VALUE, null, 2)); + assertEquals("-8.00 E", long2String(Long.MIN_VALUE + 1, null, 2)); + + final String[] zeros = {" ", ".0 ", ".00 "}; + for(int decimalPlace = 0; decimalPlace < zeros.length; decimalPlace++) { + final String trailingZeros = zeros[decimalPlace]; + + for(int e = 11; e < Long.SIZE - 1; e++) { + final TraditionalBinaryPrefix p + = TraditionalBinaryPrefix.values()[e/10 - 1]; + + { // n = 2^e + final long n = 1L << e; + final String expected = (n/p.getValue()) + " " + p.getSymbol(); + assertEquals("n=" + n, expected, long2String(n, null, 2)); + } + + { // n = 2^e + 1 + final long n = (1L << e) + 1; + final String expected = (n/p.getValue()) + trailingZeros + p.getSymbol(); + assertEquals("n=" + n, expected, long2String(n, null, decimalPlace)); + } + + { // n = 2^e - 1 + final long n = (1L << e) - 1; + final String expected = ((n+1)/p.getValue()) + trailingZeros + p.getSymbol(); + assertEquals("n=" + n, expected, long2String(n, null, decimalPlace)); + } + } + } + + assertEquals("1.50 K", long2String(3L << 9, null, 2)); + assertEquals("1.5 K", long2String(3L << 9, null, 1)); + assertEquals("1.50 M", long2String(3L << 19, null, 2)); + assertEquals("2 M", long2String(3L << 19, null, 0)); + assertEquals("3 G", long2String(3L << 30, null, 2)); + + assertEquals("0 B", byteDescription(0)); + assertEquals("-100 B", byteDescription(-100)); + assertEquals("1 KB", byteDescription(1024)); + assertEquals("1.50 KB", byteDescription(3L << 9)); + assertEquals("1.50 MB", byteDescription(3L << 19)); + assertEquals("3 GB", byteDescription(3L << 30)); + } + + private static String byteDescription(long len) { + return long2String(len, "B", 2); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/5c37675f/ratis-test/src/test/resources/log4j.properties ---------------------------------------------------------------------- diff --git a/ratis-test/src/test/resources/log4j.properties b/ratis-test/src/test/resources/log4j.properties new file mode 100644 index 0000000..ced0687 --- /dev/null +++ b/ratis-test/src/test/resources/log4j.properties @@ -0,0 +1,18 @@ +# 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. +# log4j configuration used during build and unit tests + +log4j.rootLogger=info,stdout +log4j.threshold=ALL +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p %c{2} (%F:%M(%L)) - %m%n
