BUG-5280: introduce the notion of client actor time 72/39572/39
authorRobert Varga <rovarga@cisco.com>
Sun, 29 May 2016 19:35:08 +0000 (21:35 +0200)
committerRobert Varga <rovarga@cisco.com>
Tue, 21 Jun 2016 08:45:12 +0000 (10:45 +0200)
Time is exposed as a Ticker, with the implementation returning
the system ticker.

Add a few basic tests and testin infrastructure to control
the Ticker's passage of time.

Change-Id: I2bb1a8d9555979d38434602d68d48aeef66ae78a
Signed-off-by: Robert Varga <rovarga@cisco.com>
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/actors/client/ClientActorContext.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/common/actor/MessageTrackerTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/common/actor/TestTicker.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/actors/client/AbstractClientActorTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/actors/client/ClientActorContextTest.java [new file with mode: 0644]

index ca39360..9fc17b5 100644 (file)
@@ -10,25 +10,43 @@ package org.opendaylight.controller.cluster.datastore.actors.client;
 import akka.actor.ActorRef;
 import com.google.common.annotations.Beta;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Ticker;
+import javax.annotation.Nonnull;
 import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier;
 import org.opendaylight.yangtools.concepts.Identifiable;
 
 /**
- * An actor context associated with this {@link AbstractClientActor}
+ * An actor context associated with this {@link AbstractClientActor}.
+ *
+ * Time-keeping in a client actor is based on monotonic time. The precision of this time can be expected to be the
+ * same as {@link System#nanoTime()}, but it is not tied to that particular clock. Actor clock is exposed as
+ * a {@link Ticker}, which can be obtained via {@link #ticker()}.
  *
  * @author Robert Varga
  */
 @Beta
-public final class ClientActorContext extends AbstractClientActorContext implements Identifiable<ClientIdentifier> {
+public class ClientActorContext extends AbstractClientActorContext implements Identifiable<ClientIdentifier> {
     private final ClientIdentifier identifier;
 
+    // Hidden to avoid subclassing
     ClientActorContext(final ActorRef self, final String persistenceId, final ClientIdentifier identifier) {
         super(self, persistenceId);
         this.identifier = Preconditions.checkNotNull(identifier);
     }
 
     @Override
-    public ClientIdentifier getIdentifier() {
+    public @Nonnull ClientIdentifier getIdentifier() {
         return identifier;
     }
+
+    /**
+     * Return the time ticker for this {@link ClientActorContext}. This should be used for in all time-tracking
+     * done within a client actor. Subclasses of {@link ClientActorBehavior} are encouraged to use
+     * {@link com.google.common.base.Stopwatch}.
+     *
+     * @return Client actor time source
+     */
+    public @Nonnull Ticker ticker() {
+        return Ticker.systemTicker();
+    }
 }
index 5b7d1a6..9c11c13 100644 (file)
@@ -11,7 +11,6 @@ package org.opendaylight.controller.cluster.common.actor;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
-import com.google.common.base.Ticker;
 import java.util.List;
 import org.junit.Assert;
 import org.junit.Before;
@@ -20,20 +19,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class MessageTrackerTest {
-
-    private static final class TestTicker extends Ticker {
-        private long ticks;
-
-        @Override
-        public long read() {
-            return ticks;
-        }
-
-        void increment(final long ticks) {
-            this.ticks += ticks;
-        }
-    }
-
     private static final class Foo {
         // Intentionally empty
     }
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/common/actor/TestTicker.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/common/actor/TestTicker.java
new file mode 100644 (file)
index 0000000..3387eca
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.common.actor;
+
+import com.google.common.base.Ticker;
+
+/**
+ * Utility {@link Ticker} where passage of time is explicitly controlled via {@link #increment(long)}.
+ *
+ * @author Robert Varga
+ */
+public final class TestTicker extends Ticker {
+    private long counter = 0;
+
+    @Override
+    public long read() {
+        return counter;
+    }
+
+    public long increment(final long ticks) {
+        counter += ticks;
+        return counter;
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/actors/client/AbstractClientActorTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/actors/client/AbstractClientActorTest.java
new file mode 100644 (file)
index 0000000..ee8a2a3
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.datastore.actors.client;
+
+import static org.mockito.Mockito.doReturn;
+import akka.actor.ActorRef;
+import org.junit.Before;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier;
+import org.opendaylight.controller.cluster.access.concepts.FrontendIdentifier;
+import org.opendaylight.controller.cluster.access.concepts.FrontendType;
+import org.opendaylight.controller.cluster.access.concepts.MemberName;
+import org.opendaylight.controller.cluster.common.actor.TestTicker;
+
+/**
+ * Abstract base class for client actors and their components.
+ *
+ * @author Robert Varga
+ */
+public abstract class AbstractClientActorTest {
+    private static final MemberName MEMBER_NAME = MemberName.forName("member-1");
+
+    @Mock
+    private ClientActorContext mockActorContext;
+    @Mock
+    private ActorRef mockSelf;
+
+    protected final TestTicker ticker = new TestTicker();
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        final FrontendType frontendType = FrontendType.forName(getClass().getSimpleName());
+        final FrontendIdentifier frontendId = FrontendIdentifier.create(MEMBER_NAME, frontendType);
+        final ClientIdentifier clientId = ClientIdentifier.create(frontendId, 0);
+
+        doReturn(ticker).when(mockActorContext).ticker();
+        doReturn(clientId).when(mockActorContext).getIdentifier();
+        doReturn(getClass().getSimpleName()).when(mockActorContext).persistenceId();
+        doReturn(mockSelf).when(mockActorContext).self();
+    }
+
+    protected final ClientActorContext actorContext() {
+        return mockActorContext;
+    }
+
+    protected final ActorRef self() {
+        return mockSelf;
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/actors/client/ClientActorContextTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/actors/client/ClientActorContextTest.java
new file mode 100644 (file)
index 0000000..f575815
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.datastore.actors.client;
+
+import static org.junit.Assert.assertSame;
+import akka.actor.ActorRef;
+import com.google.common.base.Ticker;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier;
+import org.opendaylight.controller.cluster.access.concepts.FrontendIdentifier;
+import org.opendaylight.controller.cluster.access.concepts.FrontendType;
+import org.opendaylight.controller.cluster.access.concepts.MemberName;
+import org.opendaylight.controller.cluster.datastore.ShardTransactionTest;
+
+public class ClientActorContextTest {
+    private static final MemberName MEMBER_NAME = MemberName.forName("member-1");
+    private static final FrontendType FRONTEND_TYPE = FrontendType.forName(ShardTransactionTest.class.getSimpleName());
+    private static final FrontendIdentifier FRONTEND_ID = FrontendIdentifier.create(MEMBER_NAME, FRONTEND_TYPE);
+    private static final ClientIdentifier CLIENT_ID = ClientIdentifier.create(FRONTEND_ID, 0);
+    private static final String PERSISTENCE_ID = ClientActorContextTest.class.getSimpleName();
+
+    @Mock
+    private ActorRef mockSelf;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testMockingControl() {
+        ClientActorContext ctx = new ClientActorContext(mockSelf, PERSISTENCE_ID, CLIENT_ID);
+        assertSame(CLIENT_ID, ctx.getIdentifier());
+        assertSame(PERSISTENCE_ID, ctx.persistenceId());
+        assertSame(mockSelf, ctx.self());
+    }
+
+    @Test
+    public void testTicker() {
+        ClientActorContext ctx = new ClientActorContext(mockSelf, PERSISTENCE_ID, CLIENT_ID);
+        assertSame(Ticker.systemTicker(), ctx.ticker());
+    }
+}