From: Mateusz Date: Wed, 27 Jan 2021 14:36:07 +0000 (+0100) Subject: JAL-3690 Introduce AlignCalcManager2 tests. X-Git-Url: http://source.jalview.org/gitweb/?p=jalview.git;a=commitdiff_plain;h=e23c809d6cd203187c5f65efd03741ec4cb637be JAL-3690 Introduce AlignCalcManager2 tests. --- diff --git a/src/jalview/api/AlignCalcManagerI2.java b/src/jalview/api/AlignCalcManagerI2.java index 22b4832..0bd6c3c 100644 --- a/src/jalview/api/AlignCalcManagerI2.java +++ b/src/jalview/api/AlignCalcManagerI2.java @@ -45,6 +45,13 @@ public interface AlignCalcManagerI2 void removeWorkerForAnnotation(AlignmentAnnotation annot); /** + * Alias of removeWorkerForAnnotation + */ + default void removeWorkersForAnnotation(AlignmentAnnotation annot) { + removeWorkerForAnnotation(annot); + } + + /** * Removes all workers of a given class. The classes are compared using * {@link Class#equals(Object)}. */ diff --git a/src/jalview/workers/AlignCalcManager2.java b/src/jalview/workers/AlignCalcManager2.java index 8340900..8d4796d 100644 --- a/src/jalview/workers/AlignCalcManager2.java +++ b/src/jalview/workers/AlignCalcManager2.java @@ -59,7 +59,7 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 { return; } - if (!isRestartable()) + if (!isRegistered()) { setEnabled(false); } @@ -70,7 +70,7 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 submit(); } - protected boolean isRestartable() + protected boolean isRegistered() { return registered.containsKey(getWorker()); } @@ -125,7 +125,7 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 getWorker().getClass().getName()), th); } finally { - if (!isRestartable()) + if (!isRegistered()) { // delete worker reference so garbage collector can remove it worker = null; @@ -217,7 +217,7 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 if (completed) { final var worker = getWorker(); - if (!isRestartable()) + if (!isRegistered()) PollableWorkerManager.super.worker = null; Cache.log.debug(format("Finalizing completed worker %s", worker.getClass().getName())); @@ -243,7 +243,7 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 task.cancel(false); executor.submit(() -> { final var worker = getWorker(); - if (!isRestartable()) + if (!isRegistered()) PollableWorkerManager.super.worker = null; if (worker != null) { @@ -314,7 +314,10 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 @Override public void removeWorker(AlignCalcWorkerI worker) { - registered.remove(worker); + if (worker.isDeletable()) + { + registered.remove(worker); + } } @Override @@ -324,7 +327,7 @@ public class AlignCalcManager2 implements AlignCalcManagerI2 { for (var worker : getWorkers()) { - if (worker.involves(annot) && worker.isDeletable()) + if (worker.involves(annot)) { removeWorker(worker); } diff --git a/test/jalview/workers/AlignCaclManager2Test.java b/test/jalview/workers/AlignCaclManager2Test.java new file mode 100644 index 0000000..343ad14 --- /dev/null +++ b/test/jalview/workers/AlignCaclManager2Test.java @@ -0,0 +1,377 @@ +package jalview.workers; + +import static org.testng.Assert.*; + +import java.util.ArrayList; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import jalview.api.AlignCalcManagerI2; +import jalview.api.AlignCalcWorkerI; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.Annotation; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceI; +import jalview.gui.AlignFrame; +import jalview.gui.JvOptionPane; + +@Test(singleThreaded = true) +public class AlignCaclManager2Test +{ + AlignFrame alignFrame; + + AlignCalcManagerI2 calcManager; + + @BeforeClass(alwaysRun = true) + public void setUpClass() + { + JvOptionPane.setInteractiveMode(false); + JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); + } + + @BeforeMethod(alwaysRun = true) + public void setUp() + { + AlignmentI al = new Alignment( + new SequenceI[] { new Sequence("Seq1", "ABC") }); + al.setDataset(null); + alignFrame = new AlignFrame(al, 3, 1); + calcManager = alignFrame.getViewport().getCalcManager(); + } + + @AfterMethod(alwaysRun = true) + public void tearDown() + { + calcManager.shutdown(); + } + + // Running workers + + @Test(groups = "Functional") + public void testStartRegisteredWorker() throws InterruptedException + { + var sentinel = new Object(); + var job = CompletableFuture.completedFuture(sentinel); + var worker = new AlignCalcWorkerMock(job); + calcManager.registerWorker(worker); + Thread.sleep(10); + assertSame(worker.getLastResult(), sentinel); + } + + @Test(groups = "Functional") + public void testIsWorking() throws InterruptedException + { + var job = new CompletableFuture(); + var worker = new AlignCalcWorkerMock(job); + calcManager.registerWorker(worker); + assertTrue(calcManager.isWorking(worker)); + assertTrue(calcManager.isWorking()); + job.complete(null); + Thread.sleep(10); + assertFalse(calcManager.isWorking(worker)); + assertFalse(calcManager.isWorking()); + } + + @Test(groups = "Functional") + public void testIsWorkingWithAnnotation() throws InterruptedException + { + var job = new CompletableFuture(); + var worker1 = new AlignCalcWorkerMock(job); + var annot = worker1.annotation = newAlignmentAnnotation(); + var otherAnnot = newAlignmentAnnotation(); + calcManager.registerWorker(worker1); + assertTrue(calcManager.isWorkingWithAnnotation(annot)); + assertFalse(calcManager.isWorkingWithAnnotation(otherAnnot)); + job.complete(null); + Thread.sleep(10); + assertFalse(calcManager.isWorkingWithAnnotation(annot)); + } + + @Test(groups = "Functional") + public void testRestartCompletedWorkers() throws Throwable + { + var sentinel1 = new Object(); + var sentinel2 = new Object(); + var job = CompletableFuture.completedFuture(sentinel1); + var worker = new AlignCalcWorkerMock(job); + calcManager.registerWorker(worker); + Thread.sleep(10); + assertSame(worker.getLastResult(), sentinel1); + job.obtrudeValue(sentinel2); + calcManager.restartWorkers(); + Thread.sleep(10); + assertSame(worker.getLastResult(), sentinel2); + } + + @Test(groups = "Functional") + public void testRestartCancelsWorkers() throws Throwable + { + var job = new CompletableFuture(); + var worker = new AlignCalcWorkerMock(job); + var sentinel = new Object(); + calcManager.registerWorker(worker); + Thread.sleep(10); + calcManager.restartWorkers(); + Thread.sleep(10); + assertTrue(worker.wasCancelled()); + job.obtrudeValue(sentinel); + Thread.sleep(10); + assertSame(worker.getLastResult(), sentinel); + } + + // Disabling workers + + @Test(groups = "Functional") + public void testDisableWorker() + { + var worker = new AlignCalcWorkerMock(null); + calcManager.registerWorker(worker); + calcManager.disableWorker(worker); + assertTrue(calcManager.isDisabled(worker)); + calcManager.enableWorker(worker); + assertFalse(calcManager.isDisabled(worker)); + } + + @Test(groups = "Functional") + public void testRestartDisabledWorker() throws InterruptedException + { + var worker = new AlignCalcWorkerMock(null); + calcManager.registerWorker(worker); + Thread.sleep(10); + assertEquals(worker.getCallCount(), 1); + calcManager.disableWorker(worker); + calcManager.restartWorkers(); + Thread.sleep(10); + assertEquals(worker.getCallCount(), 1); + calcManager.enableWorker(worker); + calcManager.restartWorkers(); + Thread.sleep(10); + assertEquals(worker.getCallCount(), 2); + } + + // Canceling workers + + @Test(groups = "Functional") + public void testCancelWorker() throws InterruptedException + { + var worker = new AlignCalcWorkerMock(new CompletableFuture<>()); + calcManager.registerWorker(worker); + Thread.sleep(10); + calcManager.cancelWorker(worker); + Thread.sleep(10); + assertTrue(worker.wasCancelled()); + } + + // One-shot workers + + @Test(groups = "Functional") + public void testStartOneShotWorker() throws InterruptedException + { + var job = CompletableFuture.completedFuture("result"); + var worker = new AlignCalcWorkerMock(job); + calcManager.startWorker(worker); + Thread.sleep(10); + assertEquals(worker.getLastResult(), "result"); + } + + @Test(groups = "Functional") + public void testCancelOneShotWorker() throws InterruptedException + { + var worker = new AlignCalcWorkerMock(new CompletableFuture<>()); + calcManager.startWorker(worker); + Thread.sleep(10); + calcManager.cancelWorker(worker); + Thread.sleep(10); + assertTrue(worker.wasCancelled()); + } + + @Test(groups = "Functional") + public void restartOneShotWorker() throws InterruptedException + { + var job = CompletableFuture.completedFuture("result1"); + var worker = new AlignCalcWorkerMock(job); + calcManager.startWorker(worker); + Thread.sleep(10); + job.obtrudeValue("result2"); + calcManager.restartWorkers(); + Thread.sleep(10); + + } + + + // Retrieving workers + + @Test(groups = "Functional") + public void testGetWorkersOfClass() throws InterruptedException + { + var worker1 = new AlignCalcWorkerMock(null); + var worker2 = new AlignCalcWorkerMock(null); + var worker3 = new AlignCalcWorkerMock(null) {}; + calcManager.registerWorker(worker1); + calcManager.registerWorker(worker2); + calcManager.registerWorker(worker3); + final var workers = calcManager + .getWorkersOfClass(AlignCalcWorkerMock.class); + assertTrue(workers.contains(worker1) && workers.contains(worker2)); + assertFalse(workers.contains(worker3)); + } + + // Removing workers + + @Test(groups = "Functional") + public void testRemoveWorker() + { + var worker = new AlignCalcWorkerMock(null); + calcManager.registerWorker(worker); + calcManager.removeWorker(worker); + assertFalse(calcManager.getWorkers().contains(worker)); + } + + @Test(groups = "Functional") + public void testRemoveWorkersOfClass() + { + var worker1 = new AlignCalcWorkerMock(null); + var worker2 = new AlignCalcWorkerMock(null); + var worker3 = new AlignCalcWorkerMock(null) {}; + calcManager.registerWorker(worker1); + calcManager.registerWorker(worker2); + calcManager.registerWorker(worker3); + calcManager.removeWorkersOfClass(worker1.getClass()); + assertFalse(calcManager.getWorkers().contains(worker1) + || calcManager.getWorkers().contains(worker2)); + assertTrue(calcManager.getWorkers().contains(worker3)); + } + + @Test(groups = "Functional") + public void testRemoveWorkersForAnnotation() + { + var worker1 = new AlignCalcWorkerMock(null); + var worker2 = new AlignCalcWorkerMock(null); + var annot = worker1.annotation = newAlignmentAnnotation(); + calcManager.registerWorker(worker1); + calcManager.registerWorker(worker2); + calcManager.removeWorkerForAnnotation(annot); + var workers = calcManager.getWorkers(); + assertFalse(workers.contains(worker1)); + assertTrue(workers.contains(worker2)); + } + + @Test(groups = "Functional") + public void testRemoveNonRemovableWorker() + { + var worker = new AlignCalcWorkerMock(null); + worker.deletable = false; + calcManager.registerWorker(worker); + calcManager.removeWorker(worker); + assertTrue(calcManager.getWorkers().contains(worker)); + } + + @Test(groups = "Functional") + public void testRemoveNonRemovableWorkersOfClass() + { + var worker1 = new AlignCalcWorkerMock(null); + var worker2 = new AlignCalcWorkerMock(null); + worker2.deletable = false; + calcManager.registerWorker(worker1); + calcManager.registerWorker(worker2); + calcManager.removeWorkersOfClass(worker1.getClass()); + var workers = calcManager.getWorkers(); + assertFalse(workers.contains(worker1)); + assertTrue(workers.contains(worker2)); + } + + private int annotationCount = 0; + + private AlignmentAnnotation newAlignmentAnnotation() + { + return new AlignmentAnnotation("Ann" + annotationCount++, "description", + new Annotation[] {}); + } +} + +class AlignCalcWorkerMock implements AlignCalcWorkerI +{ + AlignmentAnnotation annotation = null; + Future job; + ArrayList values = new ArrayList<>(); + int callCount = 0; + boolean deletable = true; + + AlignCalcWorkerMock(Future job) + { + this.job = job; + } + + public Object getLastResult() + { + return values.isEmpty() ? null : values.get(values.size() - 1); + } + + public Throwable getException() + { + var result = getLastResult(); + return (result instanceof Throwable) ? (Throwable) result : null; + } + + public int getCallCount() { + return callCount; + } + + public boolean wasCancelled() + { + return getException() instanceof InterruptedException; + } + + @Override + public boolean involves(AlignmentAnnotation annot) + { + if (annotation == null) + return false; + else + return annot == annotation; + } + + @Override + public void updateAnnotation() + { + } + + @Override + public void removeAnnotation() + { + } + + @Override + public void run() throws Throwable + { + callCount++; + if (job != null) + { + try + { + values.add(job.get()); + } + catch (InterruptedException e) { values.add(e); } + catch (CancellationException e) { + values.add(new InterruptedException()); + } catch (ExecutionException e) + { + values.add(e.getCause()); + } + } + } + + @Override + public boolean isDeletable() + { + return deletable; + } +}