aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJim Hahn <jrh3@att.com>2018-10-08 12:23:06 -0400
committerJim Hahn <jrh3@att.com>2018-10-08 13:59:36 -0400
commit50e8bf5f73677a87c4ec131a8e0357458369cbfb (patch)
tree535edcfadb27354b6ea14024e1d2d3f9744a1407
parent002e1441fc90a257737bc567c297faa4e65aaf3d (diff)
Add coverage to test-transaction
Fixed bug in test-transaction, wherein it was sometimes using the controller instead of the controller name as the key into the map. Changed test to use latch instead of sleep. Added a little more register/unregister testing. Change-Id: I2647b44cf164038d211063bd499c25af70c2d9d7 Issue-ID: POLICY-1148 Signed-off-by: Jim Hahn <jrh3@att.com>
-rw-r--r--feature-test-transaction/src/main/java/org/onap/policy/drools/testtransaction/TestTransaction.java54
-rw-r--r--feature-test-transaction/src/test/java/org/onap/policy/drools/testtransaction/TestTransactionTest.java33
-rw-r--r--feature-test-transaction/src/test/java/org/onap/policy/drools/testtransaction/TestTransactionTest2.java409
3 files changed, 470 insertions, 26 deletions
diff --git a/feature-test-transaction/src/main/java/org/onap/policy/drools/testtransaction/TestTransaction.java b/feature-test-transaction/src/main/java/org/onap/policy/drools/testtransaction/TestTransaction.java
index c778bf67..452825c0 100644
--- a/feature-test-transaction/src/main/java/org/onap/policy/drools/testtransaction/TestTransaction.java
+++ b/feature-test-transaction/src/main/java/org/onap/policy/drools/testtransaction/TestTransaction.java
@@ -65,29 +65,29 @@ class TTImpl implements TestTransaction {
@Override
public synchronized void register(PolicyController controller) {
- if (this.controllers.containsValue(controller)) {
- final TTControllerTask controllerTask = this.controllers.get(controller.getName());
- if (controllerTask.isAlive()) {
- return;
- }
-
- // continue : unregister, register operation
+ TTControllerTask controllerTask = this.controllers.get(controller.getName());
+ if (controllerTask != null && controllerTask.isAlive()) {
+ return;
}
- final TTControllerTask controllerTask = new TTControllerTask(controller);
+ // continue : unregister, register operation
+
+ controllerTask = makeControllerTask(controller);
this.controllers.put(controller.getName(), controllerTask);
}
@Override
public synchronized void unregister(PolicyController controller) {
- if (!this.controllers.containsValue(controller)) {
- return;
+ final TTControllerTask controllerTask = this.controllers.remove(controller.getName());
+ if (controllerTask != null) {
+ controllerTask.stop();
}
+ }
- final TTControllerTask controllerTask = this.controllers.get(controller.getName());
- controllerTask.stop();
+ // these may be overridden by junit tests
- this.controllers.remove(controller.getName());
+ protected TTControllerTask makeControllerTask(PolicyController controller) {
+ return new TTControllerTask(controller);
}
}
@@ -103,7 +103,7 @@ class TTControllerTask implements Runnable {
protected final PolicyController controller;
protected volatile boolean alive = true;
- protected final Thread thread = new Thread(this);
+ protected final Thread thread = makeThread(this);
public TTControllerTask(PolicyController controller) {
this.controller = controller;
@@ -123,7 +123,7 @@ class TTControllerTask implements Runnable {
this.alive = false;
this.thread.interrupt();
try {
- this.thread.join(1000);
+ joinThread(1000);
} catch (final InterruptedException e) {
logger.error("TestTransaction thread threw", e);
this.thread.interrupt();
@@ -163,13 +163,13 @@ class TTControllerTask implements Runnable {
return;
}
- if (!Thread.currentThread().isInterrupted()) {
- Thread.sleep(TestTransaction.DEFAULT_TT_TASK_SLEEP);
+ if (!getCurrentThread().isInterrupted()) {
+ doSleep(TestTransaction.DEFAULT_TT_TASK_SLEEP);
}
}
} catch (final InterruptedException e) {
logger.info("{}: stopping ...", this, e);
- Thread.currentThread().interrupt();
+ getCurrentThread().interrupt();
} catch (final IllegalArgumentException e) {
logger.error(
"{}: controller {} has not been enabled for testing: ",
@@ -245,4 +245,22 @@ class TTControllerTask implements Runnable {
builder.append("]");
return builder.toString();
}
+
+ // these may be overridden by junit tests
+
+ protected Thread makeThread(Runnable action) {
+ return new Thread(action);
+ }
+
+ protected void joinThread(long waitTimeMs) throws InterruptedException {
+ this.thread.join(waitTimeMs);
+ }
+
+ protected void doSleep(long sleepMs) throws InterruptedException {
+ Thread.sleep(sleepMs);
+ }
+
+ protected Thread getCurrentThread() {
+ return Thread.currentThread();
+ }
}
diff --git a/feature-test-transaction/src/test/java/org/onap/policy/drools/testtransaction/TestTransactionTest.java b/feature-test-transaction/src/test/java/org/onap/policy/drools/testtransaction/TestTransactionTest.java
index 09be93f9..40b1ff9d 100644
--- a/feature-test-transaction/src/test/java/org/onap/policy/drools/testtransaction/TestTransactionTest.java
+++ b/feature-test-transaction/src/test/java/org/onap/policy/drools/testtransaction/TestTransactionTest.java
@@ -29,7 +29,8 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;
import java.util.Set;
-
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import org.junit.BeforeClass;
import org.junit.Test;
import org.onap.policy.drools.persistence.SystemPersistence;
@@ -75,30 +76,46 @@ public class TestTransactionTest {
PolicyEngine.manager.createPolicyController(TEST_CONTROLLER_NAME, controllerProperties);
assertNotNull(PolicyController.factory.get(TEST_CONTROLLER_NAME));
logger.info(controller.toString());
-
- TestTransaction.manager.register(controller);
+
+ CountDownLatch latch = new CountDownLatch(1);
+
+ // use our own impl so we can decrement the latch when run() completes
+ TTImpl impl = new TTImpl() {
+ @Override
+ protected TTControllerTask makeControllerTask(PolicyController controller) {
+ return new TTControllerTask(controller) {
+ @Override
+ public void run() {
+ super.run();
+ latch.countDown();
+ }
+ };
+ }
+ };
+
+ impl.register(controller);
assertNotNull(TestTransaction.manager);
/*
* Unregistering the controller should terminate its TestTransaction thread if it hasn't already
* been terminated
*/
- TestTransaction.manager.unregister(controller);
+ impl.unregister(controller);
- Thread ttThread = this.getThread("tt-controller-task-" + TEST_CONTROLLER_NAME);
+ Thread ttThread = getThread(latch, "tt-controller-task-" + TEST_CONTROLLER_NAME);
assertEquals(null, ttThread);
}
/**
* Returns thread object based on String name.
- *
+ * @param latch indicates when the thread has finished running
* @param threadName thread name
* @return the thread
* @throws InterruptedException exception
*/
- public Thread getThread(String threadName) throws InterruptedException {
+ public Thread getThread(CountDownLatch latch, String threadName) throws InterruptedException {
// give a chance to the transaction thread to be spawned/destroyed
- Thread.sleep(5000L);
+ latch.await(5, TimeUnit.SECONDS);
final Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
for (final Thread thread : threadSet) {
diff --git a/feature-test-transaction/src/test/java/org/onap/policy/drools/testtransaction/TestTransactionTest2.java b/feature-test-transaction/src/test/java/org/onap/policy/drools/testtransaction/TestTransactionTest2.java
new file mode 100644
index 00000000..7c31ba98
--- /dev/null
+++ b/feature-test-transaction/src/test/java/org/onap/policy/drools/testtransaction/TestTransactionTest2.java
@@ -0,0 +1,409 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * feature-test-transaction
+ * ================================================================================
+ * Copyright (C) 2017-2018 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.drools.testtransaction;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EventObject;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.drools.controller.DroolsController;
+import org.onap.policy.drools.core.PolicyContainer;
+import org.onap.policy.drools.system.PolicyController;
+
+public class TestTransactionTest2 {
+
+ private static final int MAX_SLEEP_COUNT = 3;
+ private static final String EXPECTED = "expected exception";
+ private static final String CONTROLLER1 = "controller-a";
+ private static final String CONTROLLER2 = "controller-b";
+ private static final String CONTROLLER3 = "controller-c";
+ private static final String SESSION1 = "session-a";
+ private static final String SESSION2 = "session-b";
+ private static final List<String> sessions = Arrays.asList(SESSION1, SESSION2);
+ private static final List<Object> facts = Arrays.asList(0L);
+
+ private Thread theThread;
+ private PolicyController controller;
+ private PolicyController controller2;
+ private PolicyController controller3;
+ private Runnable theAction;
+ private long waitJoinMs;
+ private long doSleepMs;
+ private DroolsController drools;
+ private PolicyContainer container;
+ private Map<String, TTControllerTask> name2task;
+ private TTControllerTask task;
+ private TTControllerTask task2;
+ private TTControllerTask task3;
+ private TestTransTImplTester impl;
+
+ /**
+ * Initialize objects for each test.
+ */
+ @Before
+ public void setUp() {
+ theThread = mock(Thread.class);
+ controller = mock(PolicyController.class);
+ controller2 = mock(PolicyController.class);
+ controller3 = mock(PolicyController.class);
+ theAction = null;
+ waitJoinMs = -1;
+ doSleepMs = -1;
+ drools = mock(DroolsController.class);
+ container = mock(PolicyContainer.class);
+ task2 = mock(TTControllerTask.class);
+ task3 = mock(TTControllerTask.class);
+ name2task = new TreeMap<>();
+
+ when(drools.getSessionNames()).thenReturn(sessions);
+ when(drools.isBrained()).thenReturn(true);
+ when(drools.factQuery(anyString(), anyString(), anyString(), anyBoolean())).thenReturn(facts);
+ when(drools.getContainer()).thenReturn(container);
+
+ when(controller.getName()).thenReturn(CONTROLLER1);
+ when(controller.getDrools()).thenReturn(drools);
+ when(controller.isAlive()).thenReturn(true);
+
+ when(controller2.getName()).thenReturn(CONTROLLER2);
+ when(controller2.getDrools()).thenReturn(drools);
+ when(controller2.isAlive()).thenReturn(true);
+
+ when(controller3.getName()).thenReturn(CONTROLLER3);
+ when(controller3.getDrools()).thenReturn(drools);
+ when(controller3.isAlive()).thenReturn(true);
+
+ task = new TestTransControllerTaskTester(controller);
+
+ name2task.put(CONTROLLER1, task);
+ name2task.put(CONTROLLER2, task2);
+ name2task.put(CONTROLLER3, task3);
+
+ impl = new TestTransTImplTester();
+ }
+
+ @Test
+ public void testTestTransactionImpl() {
+ assertNotNull(TTImpl.manager);
+ }
+
+ @Test
+ public void testTestTransactionImplRegister_testTestTransactionImplUnregister() {
+ task = mock(TTControllerTask.class);
+ when(task.isAlive()).thenReturn(true);
+ name2task.put(CONTROLLER1, task);
+
+ impl.register(controller);
+ impl.register(controller2);
+
+ // re-register
+ impl.register(controller);
+
+ // re-register when task is not running
+
+ // give controller3 same name as controller1 -> task3 replaces task
+ when(controller3.getName()).thenReturn(CONTROLLER1);
+ name2task.put(CONTROLLER1, task3);
+ when(task.isAlive()).thenReturn(false);
+ impl.register(controller3);
+
+ impl.unregister(controller);
+ verify(task, never()).stop();
+ verify(task2, never()).stop();
+ verify(task3).stop();
+
+ impl.unregister(controller2);
+ verify(task2).stop();
+
+ // unregister again - stop() should not be called again
+ impl.unregister(controller3);
+ verify(task3).stop();
+
+ // unregister original controller - no stop() should be called again
+ impl.unregister(controller);
+ verify(task, never()).stop();
+ verify(task2).stop();
+ verify(task3).stop();
+ }
+
+ @Test
+ public void testTestTransactionControllerTaskFactory() throws Exception {
+ task = new TTControllerTask(controller) {
+ @Override
+ protected Thread makeThread(Runnable action) {
+ return theThread;
+ }
+
+ @Override
+ protected void joinThread(long waitTimeMs) throws InterruptedException {
+ // do nothing
+ }
+ };
+
+ task.doSleep(1);
+ assertEquals(Thread.currentThread(), task.getCurrentThread());
+ }
+
+ @Test
+ public void testTestTransactionControllerTask() {
+ assertEquals(task, theAction);
+ assertTrue(task.isAlive());
+ assertEquals(controller, task.getController());
+ assertEquals(theThread, task.getThread());
+
+ verify(theThread).start();
+ }
+
+ @Test
+ public void testTestTransactionControllerTaskGetController() {
+ assertEquals(controller, task.getController());
+ }
+
+ @Test
+ public void testTestTransactionControllerTaskGetThread() {
+ assertEquals(theThread, task.getThread());
+ }
+
+ @Test
+ public void testTestTransactionControllerTaskStop() throws Exception {
+ task.stop();
+ assertFalse(task.isAlive());
+ verify(theThread).interrupt();
+ assertTrue(waitJoinMs > 0);
+
+ // throw interrupt during join()
+ setUp();
+ task = new TestTransControllerTaskTester(controller) {
+ @Override
+ protected void joinThread(long waitTimeMs) throws InterruptedException {
+ waitJoinMs = waitTimeMs;
+ throw new InterruptedException(EXPECTED);
+ }
+ };
+ task.stop();
+ assertFalse(task.isAlive());
+ verify(theThread, times(2)).interrupt();
+ assertTrue(waitJoinMs > 0);
+ }
+
+ @Test
+ public void testTestTransactionControllerTaskRun() {
+ task.run();
+ assertFalse(task.isAlive());
+ verify(theThread, never()).interrupt();
+ verify(controller, times(MAX_SLEEP_COUNT + 1)).isAlive();
+ assertTrue(doSleepMs > 0);
+
+ // not brained
+ setUp();
+ when(drools.isBrained()).thenReturn(false);
+ task.run();
+ assertFalse(task.isAlive());
+ verify(controller, never()).isAlive();
+ assertEquals(-1, doSleepMs);
+
+ // controller not running
+ setUp();
+ when(controller.isAlive()).thenReturn(false);
+ task.run();
+ assertFalse(task.isAlive());
+ assertEquals(-1, doSleepMs);
+
+ // controller is locked
+ setUp();
+ when(controller.isLocked()).thenReturn(true);
+ task.run();
+ assertFalse(task.isAlive());
+ assertEquals(-1, doSleepMs);
+
+ // un-brain during sleep
+ setUp();
+ task = new TestTransControllerTaskTester(controller) {
+ @Override
+ protected void doSleep(long sleepMs) throws InterruptedException {
+ when(drools.isBrained()).thenReturn(false);
+ super.doSleep(sleepMs);
+ }
+ };
+ task.run();
+ assertFalse(task.isAlive());
+ // only hit top of the loop twice
+ verify(controller, times(2)).isAlive();
+ assertTrue(doSleepMs > 0);
+
+ // stop during sleep
+ setUp();
+ task = new TestTransControllerTaskTester(controller) {
+ @Override
+ protected void doSleep(long sleepMs) throws InterruptedException {
+ task.stop();
+ super.doSleep(sleepMs);
+ }
+ };
+ task.run();
+ assertFalse(task.isAlive());
+ // only hit top of the loop twice
+ verify(controller, times(2)).isAlive();
+ assertTrue(doSleepMs > 0);
+
+ // isInterrupted() returns true the first time, interrupt next time
+ setUp();
+ AtomicInteger count = new AtomicInteger(1);
+ when(theThread.isInterrupted()).thenAnswer(args -> {
+ if (count.decrementAndGet() >= 0) {
+ return true;
+ } else {
+ throw new InterruptedException(EXPECTED);
+ }
+ });
+ task.run();
+ assertFalse(task.isAlive());
+ verify(controller, times(2)).isAlive();
+ // doSleep() should not be called
+ assertEquals(-1, doSleepMs);
+
+ // interrupt during sleep
+ setUp();
+ task = new TestTransControllerTaskTester(controller) {
+ @Override
+ protected void doSleep(long sleepMs) throws InterruptedException {
+ super.doSleep(sleepMs);
+ throw new InterruptedException(EXPECTED);
+ }
+ };
+ task.run();
+ assertFalse(task.isAlive());
+ verify(theThread).interrupt();
+ // only hit top of the loop once
+ verify(controller).isAlive();
+ assertTrue(doSleepMs > 0);
+
+ // stop() during factQuery()
+ setUp();
+ when(drools.factQuery(anyString(), anyString(), anyString(), anyBoolean())).thenAnswer(args -> {
+ task.stop();
+ return facts;
+ });
+ task.run();
+ assertFalse(task.isAlive());
+ // only hit top of the loop once
+ verify(controller).isAlive();
+
+ // exception during isBrained() check
+ setUp();
+ when(drools.isBrained()).thenThrow(new IllegalArgumentException(EXPECTED));
+ task.run();
+ assertFalse(task.isAlive());
+
+ // other exception during isBrained() check
+ setUp();
+ when(drools.isBrained()).thenThrow(new RuntimeException(EXPECTED));
+ task.run();
+ assertFalse(task.isAlive());
+ }
+
+ @Test
+ public void testTestTransactionControllerTaskInjectTxIntoSessions() {
+ task.run();
+ verify(container, times(MAX_SLEEP_COUNT * sessions.size())).insert(anyString(), any(EventObject.class));
+
+ // null facts
+ setUp();
+ when(drools.factQuery(anyString(), anyString(), anyString(), anyBoolean())).thenReturn(null);
+ task.run();
+ verify(container, never()).insert(anyString(), any());
+
+ // empty fact list
+ setUp();
+ when(drools.factQuery(anyString(), anyString(), anyString(), anyBoolean())).thenReturn(Collections.emptyList());
+ task.run();
+ verify(container, never()).insert(anyString(), any());
+ }
+
+ @Test
+ public void testTestTransactionControllerTaskToString() {
+ assertTrue(task.toString().startsWith("TTControllerTask ["));
+ }
+
+ /**
+ * TestTransaction with overridden methods.
+ */
+ private class TestTransTImplTester extends TTImpl {
+
+ @Override
+ protected TTControllerTask makeControllerTask(PolicyController controller) {
+ return name2task.get(controller.getName());
+ }
+ }
+
+ /**
+ * Controller task with overridden methods.
+ */
+ private class TestTransControllerTaskTester extends TTControllerTask {
+ private int sleepCount = MAX_SLEEP_COUNT;
+
+ public TestTransControllerTaskTester(PolicyController controller) {
+ super(controller);
+ }
+
+ @Override
+ protected Thread makeThread(Runnable action) {
+ theAction = action;
+ return theThread;
+ }
+
+ @Override
+ protected void joinThread(long waitTimeMs) throws InterruptedException {
+ waitJoinMs = waitTimeMs;
+ }
+
+ @Override
+ protected void doSleep(long sleepMs) throws InterruptedException {
+ doSleepMs = sleepMs;
+
+ if (--sleepCount <= 0) {
+ when(controller.isAlive()).thenReturn(false);
+ }
+ }
+
+ @Override
+ protected Thread getCurrentThread() {
+ return thread;
+ }
+ }
+}