diff options
author | Jim Hahn <jrh3@att.com> | 2020-08-20 10:11:54 -0400 |
---|---|---|
committer | Jim Hahn <jrh3@att.com> | 2020-08-20 11:00:06 -0400 |
commit | 56efff004af2d1be64c67f7c8091cb4553a0e86b (patch) | |
tree | e75f3248c9a25f62a5d2a9483616cfafad4927ad /controlloop/common/eventmanager/src/test/java | |
parent | 6656a9fc98c178351122fd326940fbb6923d292f (diff) |
Add generic eventmanager classes
Added classes that are event-agnostic and support moving control from
java into rules.
Updates per review comments:
- removed policy scope
Issue-ID: POLICY-2748-event-mgr
Change-Id: Icf811cc25a3975543fc5c725766b7b9df2bb87b0
Signed-off-by: Jim Hahn <jrh3@att.com>
Diffstat (limited to 'controlloop/common/eventmanager/src/test/java')
2 files changed, 675 insertions, 0 deletions
diff --git a/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManagerTest.java b/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManagerTest.java new file mode 100644 index 000000000..91434d7ea --- /dev/null +++ b/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManagerTest.java @@ -0,0 +1,295 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.eventmanager; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.policy.common.utils.coder.Coder; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardYamlCoder; +import org.onap.policy.common.utils.io.Serializer; +import org.onap.policy.common.utils.resources.ResourceUtils; +import org.onap.policy.controlloop.ControlLoopException; +import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome; +import org.onap.policy.controlloop.drl.legacy.ControlLoopParams; +import org.onap.policy.controlloop.policy.PolicyResult; +import org.onap.policy.controlloop.policy.Target; +import org.onap.policy.controlloop.policy.TargetType; +import org.onap.policy.drools.core.lock.LockCallback; +import org.onap.policy.drools.core.lock.LockImpl; +import org.onap.policy.drools.core.lock.LockState; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; + +public class ControlLoopEventManagerTest { + private static final UUID REQ_ID = UUID.randomUUID(); + private static final String CL_NAME = "my-closed-loop-name"; + private static final String POLICY_NAME = "my-policy-name"; + private static final String POLICY_SCOPE = "my-scope"; + private static final String POLICY_VERSION = "1.2.3"; + private static final String LOCK1 = "my-lock-A"; + private static final String LOCK2 = "my-lock-B"; + private static final Coder yamlCoder = new StandardYamlCoder(); + private static final String MY_KEY = "def"; + + @Mock + private ExecutorService executor; + + private long preCreateTimeMs; + private List<LockImpl> locks; + private Target target; + private ToscaPolicy tosca; + private ControlLoopParams params; + private ControlLoopEventManager mgr; + + /** + * Sets up. + */ + @Before + public void setUp() throws ControlLoopException, CoderException { + MockitoAnnotations.initMocks(this); + + target = new Target(); + target.setType(TargetType.VNF); + + params = new ControlLoopParams(); + params.setClosedLoopControlName(CL_NAME); + params.setPolicyName(POLICY_NAME); + params.setPolicyScope(POLICY_SCOPE); + params.setPolicyVersion(POLICY_VERSION); + + loadPolicy("eventManager/event-mgr-simple.yaml"); + + locks = new ArrayList<>(); + + preCreateTimeMs = System.currentTimeMillis(); + + MyManager.executor = executor; + MyManager.locks = locks; + + mgr = new MyManager(params, REQ_ID); + } + + @Test + public void testConstructor() { + assertEquals(POLICY_NAME, mgr.getPolicyName()); + + assertTrue(mgr.isActive()); + assertEquals(CL_NAME, mgr.getClosedLoopControlName()); + assertSame(REQ_ID, mgr.getRequestId()); + assertEquals(POLICY_NAME, mgr.getPolicyName()); + assertEquals(POLICY_VERSION, mgr.getPolicyVersion()); + assertNotNull(mgr.getProcessor()); + assertThat(mgr.getEndTimeMs()).isGreaterThanOrEqualTo(preCreateTimeMs); + } + + @Test + public void testGetCreateCount() throws ControlLoopException { + long original = ControlLoopEventManager.getCreateCount(); + + new MyManager(params, REQ_ID); + assertEquals(original + 1, ControlLoopEventManager.getCreateCount()); + + new MyManager(params, REQ_ID); + assertEquals(original + 2, ControlLoopEventManager.getCreateCount()); + } + + @Test + public void testIsActive() throws Exception { + mgr = new ControlLoopEventManager(params, REQ_ID); + assertTrue(mgr.isActive()); + + ControlLoopEventManager mgr2 = Serializer.roundTrip(mgr); + assertFalse(mgr2.isActive()); + } + + @Test + public void testDestroy() throws IOException { + mgr.requestLock(LOCK1); + mgr.requestLock(LOCK2); + mgr.requestLock(LOCK1); + + // ensure destroy() doesn't throw an exception if the object is deserialized + ControlLoopEventManager mgr2 = Serializer.roundTrip(mgr); + assertThatCode(() -> mgr2.destroy()).doesNotThrowAnyException(); + + // locks should not have been freed + for (LockImpl lock : locks) { + assertFalse(lock.isUnavailable()); + } + + mgr.destroy(); + + runExecutor(); + + for (LockImpl lock : locks) { + assertTrue(lock.isUnavailable()); + } + } + + @Test + public void testDetmControlLoopTimeoutMs() throws Exception { + long timeMs = 1200 * 1000L; + long end = mgr.getEndTimeMs(); + assertThat(end).isGreaterThanOrEqualTo(preCreateTimeMs + timeMs); + assertThat(end).isLessThan(preCreateTimeMs + timeMs + 5000); + } + + @Test + public void testRequestLock() { + final CompletableFuture<OperationOutcome> future1 = mgr.requestLock(LOCK1); + assertTrue(mgr.getOutcomes().isEmpty()); + + final CompletableFuture<OperationOutcome> future2 = mgr.requestLock(LOCK2); + assertTrue(mgr.getOutcomes().isEmpty()); + + assertSame(future1, mgr.requestLock(LOCK1)); + assertTrue(mgr.getOutcomes().isEmpty()); + + assertEquals(2, locks.size()); + + assertTrue(future1.isDone()); + assertTrue(future2.isDone()); + + // indicate that the first lock failed + locks.get(0).notifyUnavailable(); + + verifyLock(PolicyResult.FAILURE); + assertTrue(mgr.getOutcomes().isEmpty()); + } + + private void verifyLock(PolicyResult result) { + OperationOutcome outcome = mgr.getOutcomes().poll(); + assertNotNull(outcome); + assertEquals(ActorConstants.LOCK_ACTOR, outcome.getActor()); + assertEquals(ActorConstants.LOCK_OPERATION, outcome.getOperation()); + assertNotNull(outcome.getEnd()); + assertTrue(outcome.isFinalOutcome()); + assertEquals(result, outcome.getResult()); + } + + @Test + public void testOnStart() { + OperationOutcome outcome1 = new OperationOutcome(); + OperationOutcome outcome2 = new OperationOutcome(); + + mgr.onStart(outcome1); + mgr.onStart(outcome2); + + assertSame(outcome1, mgr.getOutcomes().poll()); + assertSame(outcome2, mgr.getOutcomes().poll()); + assertTrue(mgr.getOutcomes().isEmpty()); + } + + @Test + public void testOnComplete() { + OperationOutcome outcome1 = new OperationOutcome(); + OperationOutcome outcome2 = new OperationOutcome(); + + mgr.onComplete(outcome1); + mgr.onComplete(outcome2); + + assertSame(outcome1, mgr.getOutcomes().poll()); + assertSame(outcome2, mgr.getOutcomes().poll()); + assertTrue(mgr.getOutcomes().isEmpty()); + } + + @Test + public void testContains_testGetProperty_testSetProperty_testRemoveProperty() { + mgr.setProperty("abc", "a string"); + mgr.setProperty(MY_KEY, 100); + + assertTrue(mgr.contains(MY_KEY)); + assertFalse(mgr.contains("ghi")); + + String strValue = mgr.getProperty("abc"); + assertEquals("a string", strValue); + + int intValue = mgr.getProperty(MY_KEY); + assertEquals(100, intValue); + + mgr.removeProperty(MY_KEY); + assertFalse(mgr.contains(MY_KEY)); + } + + @Test + public void testToString() { + assertNotNull(mgr.toString()); + } + + private void loadPolicy(String fileName) throws CoderException { + ToscaServiceTemplate template = + yamlCoder.decode(ResourceUtils.getResourceAsString(fileName), ToscaServiceTemplate.class); + tosca = template.getToscaTopologyTemplate().getPolicies().get(0).values().iterator().next(); + + params.setToscaPolicy(tosca); + } + + private void runExecutor() { + ArgumentCaptor<Runnable> runCaptor = ArgumentCaptor.forClass(Runnable.class); + verify(executor).execute(runCaptor.capture()); + + runCaptor.getValue().run(); + } + + + private static class MyManager extends ControlLoopEventManager { + private static final long serialVersionUID = 1L; + + private static ExecutorService executor; + private static List<LockImpl> locks; + + public MyManager(ControlLoopParams params, UUID requestId) + throws ControlLoopException { + super(params, requestId); + } + + @Override + protected ExecutorService getBlockingExecutor() { + return executor; + } + + @Override + protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) { + LockImpl lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback); + locks.add(lock); + callback.lockAvailable(lock); + } + } +} diff --git a/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/StepTest.java b/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/StepTest.java new file mode 100644 index 000000000..1472adc18 --- /dev/null +++ b/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/StepTest.java @@ -0,0 +1,380 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.eventmanager; + +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.time.Instant; +import java.util.Map; +import java.util.TreeMap; +import java.util.UUID; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.policy.controlloop.VirtualControlLoopEvent; +import org.onap.policy.controlloop.actorserviceprovider.ActorService; +import org.onap.policy.controlloop.actorserviceprovider.Operation; +import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome; +import org.onap.policy.controlloop.actorserviceprovider.Operator; +import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext; +import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams; +import org.onap.policy.controlloop.actorserviceprovider.spi.Actor; +import org.onap.policy.controlloop.policy.Policy; +import org.onap.policy.controlloop.policy.PolicyResult; +import org.onap.policy.controlloop.policy.Target; +import org.onap.policy.controlloop.policy.TargetType; + +public class StepTest { + private static final UUID REQ_ID = UUID.randomUUID(); + private static final String POLICY_ID = "my-policy"; + private static final String POLICY_ACTOR = "my-actor"; + private static final String POLICY_OPERATION = "my-operation"; + private static final String MY_TARGET = "my-target"; + private static final String PAYLOAD_KEY = "payload-key"; + private static final String PAYLOAD_VALUE = "payload-value"; + private static final long REMAINING_MS = 5000; + private static final Integer POLICY_RETRY = 3; + private static final Integer POLICY_TIMEOUT = 20; + private static final String EXPECTED_EXCEPTION = "expected exception"; + + @Mock + private Operator policyOperator; + @Mock + private Operation policyOperation; + @Mock + private Actor policyActor; + @Mock + private ActorService actors; + + private CompletableFuture<OperationOutcome> future; + private Target target; + private Map<String, String> payload; + private Policy policy; + private VirtualControlLoopEvent event; + private ControlLoopEventContext context; + private BlockingQueue<OperationOutcome> starts; + private BlockingQueue<OperationOutcome> completions; + private ControlLoopOperationParams params; + private AtomicReference<Instant> startTime; + private Step step; + + /** + * Sets up. + */ + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + future = new CompletableFuture<>(); + + // configure policy operation + when(actors.getActor(POLICY_ACTOR)).thenReturn(policyActor); + when(policyActor.getOperator(POLICY_OPERATION)).thenReturn(policyOperator); + when(policyOperator.buildOperation(any())).thenReturn(policyOperation); + when(policyOperation.start()).thenReturn(future); + + target = new Target(); + target.setType(TargetType.VM); + + payload = Map.of(PAYLOAD_KEY, PAYLOAD_VALUE); + + policy = new Policy(); + policy.setId(POLICY_ID); + policy.setActor(POLICY_ACTOR); + policy.setRecipe(POLICY_OPERATION); + policy.setTarget(target); + policy.setPayload(payload); + policy.setRetry(POLICY_RETRY); + policy.setTimeout(POLICY_TIMEOUT); + + event = new VirtualControlLoopEvent(); + event.setRequestId(REQ_ID); + event.setTarget(ControlLoopOperationManager2.VSERVER_VSERVER_NAME); + event.setAai(new TreeMap<>(Map.of(ControlLoopOperationManager2.VSERVER_VSERVER_NAME, MY_TARGET))); + + context = new ControlLoopEventContext(event); + + starts = new LinkedBlockingQueue<>(); + completions = new LinkedBlockingQueue<>(); + + params = ControlLoopOperationParams.builder().actor(POLICY_ACTOR).actorService(actors) + .completeCallback(completions::add).context(context).executor(ForkJoinPool.commonPool()) + .operation(POLICY_OPERATION).payload(new TreeMap<>(payload)).startCallback(starts::add) + .target(target).targetEntity(MY_TARGET).build(); + + startTime = new AtomicReference<>(); + + step = new Step(params, startTime); + } + + @Test + public void testConstructor() { + assertTrue(step.isPolicyStep()); + assertSame(params, step.getParams()); + + // check that it recorded the startTime by starting and checking it + step.init(); + step.start(REMAINING_MS); + + assertNotNull(startTime.get()); + + // try with null start time + assertThatThrownBy(() -> new Step(params, null)).isInstanceOf(NullPointerException.class) + .hasMessageContaining("startTime"); + } + + @Test + public void testConstructorWithOtherStep_testInitStartTime_testGetStartTimeRef() { + Step step2 = new Step(step, "actorB", "operB"); + assertFalse(step2.isPolicyStep()); + + ControlLoopOperationParams params2 = step2.getParams(); + assertEquals("actorB", params2.getActor()); + assertEquals("operB", params2.getOperation()); + assertNull(params2.getRetry()); + assertNull(params2.getTimeoutSec()); + assertSame(target, params2.getTarget()); + assertEquals(MY_TARGET, params2.getTargetEntity()); + assertTrue(params2.getPayload().isEmpty()); + + when(actors.getActor(params2.getActor())).thenReturn(policyActor); + when(policyActor.getOperator(params2.getOperation())).thenReturn(policyOperator); + + assertNull(step2.getStartTime()); + + // check that it recorded the startTime by starting and checking it + step2.init(); + step2.start(REMAINING_MS); + + Instant instant = startTime.get(); + assertNotNull(instant); + assertSame(instant, step2.getStartTime()); + + // launch the original step, too, so we can test the other branch of + // initStartTime() + step.init(); + step.start(REMAINING_MS); + + assertSame(instant, startTime.get()); + assertSame(instant, step.getStartTime()); + } + + @Test + public void testGetActorName_testGetOperationName() { + assertEquals(POLICY_ACTOR, step.getActorName()); + assertEquals(POLICY_OPERATION, step.getOperationName()); + } + + @Test + public void testIsInitialized_testInit_testGetOperation() { + assertFalse(step.isInitialized()); + + // verify it's unchanged + assertFalse(step.isInitialized()); + + assertNull(step.getOperation()); + + step.init(); + + assertSame(policyOperation, step.getOperation()); + assertTrue(step.isInitialized()); + + // repeat - should be unchanged + step.init(); + assertSame(policyOperation, step.getOperation()); + assertTrue(step.isInitialized()); + + // repeat without init - should be unchanged + assertSame(policyOperation, step.getOperation()); + assertTrue(step.isInitialized()); + } + + @Test + public void testStart() { + assertThatIllegalStateException().isThrownBy(() -> step.start(REMAINING_MS)) + .withMessage("step has not been initialized"); + + // initialize it, by calling getOperation(), and then try again + step.init(); + assertTrue(step.start(REMAINING_MS)); + + assertNotNull(startTime.get()); + + // should fail if we try again + assertThatIllegalStateException().isThrownBy(() -> step.start(REMAINING_MS)) + .withMessage("step is already running"); + } + + /** + * Tests start() when the operation.start() throws an exception. + */ + @Test + public void testStartException() { + when(policyOperation.start()).thenThrow(new RuntimeException()); + step.init(); + + assertTrue(step.start(REMAINING_MS)); + + // exception should be immediate + OperationOutcome outcome = completions.poll(); + assertNotNull(outcome); + + assertNotEquals(PolicyResult.SUCCESS, outcome.getResult()); + assertEquals(POLICY_ACTOR, outcome.getActor()); + assertTrue(outcome.isFinalOutcome()); + } + + /** + * Tests start() when the operation throws an asynchronous exception. + */ + @Test + public void testStartAsyncException() { + step.init(); + step.start(REMAINING_MS); + + future.completeExceptionally(new RuntimeException(EXPECTED_EXCEPTION)); + + // exception should be immediate + OperationOutcome outcome = completions.poll(); + assertNotNull(outcome); + + assertNotEquals(PolicyResult.SUCCESS, outcome.getResult()); + assertEquals(POLICY_ACTOR, outcome.getActor()); + assertTrue(outcome.isFinalOutcome()); + } + + /** + * Tests handleException() when the exception is a CancellationException. + */ + @Test + public void testHandleExceptionCancellationException() { + step.init(); + step.start(REMAINING_MS); + + future.completeExceptionally(new CancellationException(EXPECTED_EXCEPTION)); + + // should not have generated an outcome + assertNull(completions.peek()); + } + + @Test + public void testHandleExceptionCauseCancellationException() { + step.init(); + step.start(REMAINING_MS); + + future.completeExceptionally(new RuntimeException(EXPECTED_EXCEPTION, new CancellationException())); + + // should not have generated an outcome + assertNull(completions.peek()); + } + + @Test + public void testHandleException() { + when(policyOperation.start()).thenThrow(new RuntimeException()); + + step.init(); + + assertTrue(step.start(REMAINING_MS)); + + // exception should be immediate + OperationOutcome outcome = completions.poll(); + assertNotNull(outcome); + + assertNotEquals(PolicyResult.SUCCESS, outcome.getResult()); + assertEquals(POLICY_ACTOR, outcome.getActor()); + assertTrue(outcome.isFinalOutcome()); + assertEquals(POLICY_OPERATION, outcome.getOperation()); + assertSame(startTime.get(), outcome.getStart()); + assertNotNull(outcome.getEnd()); + assertTrue(outcome.getEnd().getEpochSecond() >= startTime.get().getEpochSecond()); + } + + @Test + public void testHandleTimeout() throws InterruptedException { + step.init(); + + long tstart = System.currentTimeMillis(); + + // give it a short timeout + step.start(100); + + OperationOutcome outcome = completions.poll(5, TimeUnit.SECONDS); + assertNotNull(outcome); + + // should not have timed out before 100ms + assertTrue(tstart + 100 <= System.currentTimeMillis()); + + // must wait for the future to complete before checking that it was cancelled + assertThatThrownBy(() -> future.get(5, TimeUnit.SECONDS)).isInstanceOf(Exception.class); + + // verify that the future was cancelled + assertTrue(future.isCancelled()); + + assertNotEquals(PolicyResult.SUCCESS, outcome.getResult()); + assertEquals(ActorConstants.CL_TIMEOUT_ACTOR, outcome.getActor()); + assertTrue(outcome.isFinalOutcome()); + assertNull(outcome.getOperation()); + assertSame(startTime.get(), outcome.getStart()); + assertNotNull(outcome.getEnd()); + assertTrue(outcome.getEnd().getEpochSecond() >= startTime.get().getEpochSecond()); + } + + @Test + public void testCancel() { + // should have no effect + step.cancel(); + + step.init(); + + step.start(REMAINING_MS); + step.cancel(); + + assertTrue(future.isCancelled()); + } + + @Test + public void testBuildOperation() { + assertSame(policyOperation, step.buildOperation()); + } + + @Test + public void testToString() { + assertNotNull(step.toString()); + } +} |