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; } }