/** * 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.openflowplugin.applications.frsync.impl; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.runners.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.openflowplugin.applications.frsync.SyncReactor; import org.opendaylight.openflowplugin.applications.frsync.util.SyncupEntry; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Test for {@link SyncReactorFutureZipDecorator}. */ @RunWith(MockitoJUnitRunner.class) public class SyncReactorFutureZipDecoratorTest { private static final Logger LOG = LoggerFactory.getLogger(SyncReactorFutureZipDecoratorTest.class); private static final NodeId NODE_ID = new NodeId("testNode"); private SyncReactorFutureZipDecorator reactor; private InstanceIdentifier<FlowCapableNode> fcNodePath; private ListeningExecutorService syncThreadPool; private final LogicalDatastoreType configDS = LogicalDatastoreType.CONFIGURATION; private final LogicalDatastoreType operationalDS = LogicalDatastoreType.OPERATIONAL; @Mock private SyncReactor delegate; @Mock private SyncupEntry syncupEntry; @Before public void setUp() { final ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder() .setDaemon(false) .setNameFormat("frsync-test-%d") .setUncaughtExceptionHandler((thread, e) -> LOG.error("Uncaught exception {}", thread, e)) .build()); syncThreadPool = MoreExecutors.listeningDecorator(executorService); reactor = new SyncReactorFutureZipDecorator(delegate, syncThreadPool); fcNodePath = InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(NODE_ID)) .augmentation(FlowCapableNode.class); } @Test public void testSyncupWithOptimizedConfigDeltaCompression() throws Exception { final FlowCapableNode dataBefore = Mockito.mock(FlowCapableNode.class); final FlowCapableNode dataAfter = Mockito.mock(FlowCapableNode.class); final FlowCapableNode dataAfter2 = Mockito.mock(FlowCapableNode.class); final CountDownLatch latchForFirst = new CountDownLatch(1); final CountDownLatch latchForNext = new CountDownLatch(1); final SyncupEntry first = new SyncupEntry(dataBefore, configDS, null, operationalDS); final SyncupEntry second = new SyncupEntry(dataAfter, configDS, dataBefore, configDS); final SyncupEntry third = new SyncupEntry(null, configDS, dataAfter, configDS); final SyncupEntry fourth = new SyncupEntry(dataAfter2, configDS, null, configDS); final SyncupEntry zipped = new SyncupEntry(dataAfter2, configDS, dataBefore, configDS); final List<ListenableFuture<Boolean>> allResults = new ArrayList<>(); Mockito.when(delegate.syncup(Matchers.<InstanceIdentifier<FlowCapableNode>>any(), Mockito.eq(first))) .thenAnswer(new Answer<ListenableFuture<Boolean>>() { @Override public ListenableFuture<Boolean> answer(final InvocationOnMock invocationOnMock) throws Throwable { LOG.info("unlocking next configs"); latchForNext.countDown(); latchForFirst.await(); LOG.info("unlocking first delegate"); return Futures.immediateFuture(Boolean.TRUE); } }); allResults.add(reactor.syncup(fcNodePath, first)); latchForNext.await(); mockSyncupWithEntry(second); allResults.add(reactor.syncup(fcNodePath, second)); mockSyncupWithEntry(third); allResults.add(reactor.syncup(fcNodePath, third)); mockSyncupWithEntry(fourth); allResults.add(reactor.syncup(fcNodePath, fourth)); latchForFirst.countDown(); Futures.allAsList(allResults).get(1, TimeUnit.SECONDS); LOG.info("all configs done"); syncThreadPool.shutdown(); boolean terminated = syncThreadPool.awaitTermination(1, TimeUnit.SECONDS); if (!terminated) { LOG.info("thread pool not terminated."); syncThreadPool.shutdownNow(); } final InOrder inOrder = Mockito.inOrder(delegate); inOrder.verify(delegate).syncup(fcNodePath, first); inOrder.verify(delegate).syncup(fcNodePath, zipped); inOrder.verifyNoMoreInteractions(); } @Test public void testSyncupConfigEmptyQueue() throws Exception { final FlowCapableNode dataBefore = Mockito.mock(FlowCapableNode.class); final FlowCapableNode dataAfter = Mockito.mock(FlowCapableNode.class); final CountDownLatch latchForNext = new CountDownLatch(1); final SyncupEntry first = new SyncupEntry(dataBefore, configDS, null, configDS); final SyncupEntry second = new SyncupEntry(dataAfter, configDS, dataBefore, configDS); Mockito.when(delegate.syncup(Matchers.<InstanceIdentifier<FlowCapableNode>>any(), Mockito.eq(first))) .thenAnswer(new Answer<ListenableFuture<Boolean>>() { @Override public ListenableFuture<Boolean> answer(final InvocationOnMock invocationOnMock) throws Throwable { LOG.info("unlocking next config"); latchForNext.countDown(); return Futures.immediateFuture(Boolean.TRUE); } }); reactor.syncup(fcNodePath, first); latchForNext.await(); mockSyncupWithEntry(second); reactor.syncup(fcNodePath, second); boolean terminated = syncThreadPool.awaitTermination(1, TimeUnit.SECONDS); if (!terminated) { LOG.info("thread pool not terminated."); syncThreadPool.shutdownNow(); } final InOrder inOrder = Mockito.inOrder(delegate); inOrder.verify(delegate).syncup(fcNodePath, first); inOrder.verify(delegate).syncup(fcNodePath, second); inOrder.verifyNoMoreInteractions(); } @Test public void testSyncupRewriteZipEntryWithOperationalDelta() throws Exception { final FlowCapableNode configBefore = Mockito.mock(FlowCapableNode.class); final FlowCapableNode configAfter = Mockito.mock(FlowCapableNode.class); final FlowCapableNode configActual = Mockito.mock(FlowCapableNode.class); final FlowCapableNode freshOperational = Mockito.mock(FlowCapableNode.class); final CountDownLatch latchForFirst = new CountDownLatch(1); final CountDownLatch latchForNext = new CountDownLatch(1); final SyncupEntry first = new SyncupEntry(configAfter, configDS, configBefore, configDS); final SyncupEntry second = new SyncupEntry(configActual, configDS, freshOperational, operationalDS); Mockito.when(delegate.syncup(Matchers.<InstanceIdentifier<FlowCapableNode>>any(), Mockito.eq(first))) .thenAnswer(new Answer<ListenableFuture<Boolean>>() { @Override public ListenableFuture<Boolean> answer(final InvocationOnMock invocationOnMock) throws Throwable { LOG.info("unlocking for fresh operational"); latchForNext.countDown(); latchForFirst.await(); LOG.info("unlocking first delegate"); return Futures.immediateFuture(Boolean.TRUE); } }); reactor.syncup(fcNodePath, first); latchForNext.await(); mockSyncupWithEntry(second); reactor.syncup(fcNodePath, second); latchForFirst.countDown(); syncThreadPool.shutdown(); boolean terminated = syncThreadPool.awaitTermination(1, TimeUnit.SECONDS); if (!terminated) { LOG.info("thread pool not terminated."); syncThreadPool.shutdownNow(); } Mockito.verify(delegate, Mockito.times(1)).syncup(fcNodePath, second); } private void mockSyncupWithEntry(final SyncupEntry entry) { Mockito.when(delegate.syncup(Matchers.any(), Mockito.eq(entry))) .thenReturn(Futures.immediateFuture(Boolean.TRUE)); } @After public void tearDown() { syncThreadPool.shutdownNow(); } }