JAL-3690 Introduce AlignCalcManager2 tests.
[jalview.git] / test / jalview / workers / AlignCaclManager2Test.java
1 package jalview.workers;
2
3 import static org.testng.Assert.*;
4
5 import java.util.ArrayList;
6 import java.util.concurrent.CancellationException;
7 import java.util.concurrent.CompletableFuture;
8 import java.util.concurrent.ExecutionException;
9 import java.util.concurrent.Future;
10 import org.testng.annotations.AfterMethod;
11 import org.testng.annotations.BeforeClass;
12 import org.testng.annotations.BeforeMethod;
13 import org.testng.annotations.Test;
14
15 import jalview.api.AlignCalcManagerI2;
16 import jalview.api.AlignCalcWorkerI;
17 import jalview.datamodel.Alignment;
18 import jalview.datamodel.AlignmentAnnotation;
19 import jalview.datamodel.AlignmentI;
20 import jalview.datamodel.Annotation;
21 import jalview.datamodel.Sequence;
22 import jalview.datamodel.SequenceI;
23 import jalview.gui.AlignFrame;
24 import jalview.gui.JvOptionPane;
25
26 @Test(singleThreaded = true)
27 public class AlignCaclManager2Test
28 {
29   AlignFrame alignFrame;
30
31   AlignCalcManagerI2 calcManager;
32
33   @BeforeClass(alwaysRun = true)
34   public void setUpClass()
35   {
36     JvOptionPane.setInteractiveMode(false);
37     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
38   }
39
40   @BeforeMethod(alwaysRun = true)
41   public void setUp()
42   {
43     AlignmentI al = new Alignment(
44         new SequenceI[] { new Sequence("Seq1", "ABC") });
45     al.setDataset(null);
46     alignFrame = new AlignFrame(al, 3, 1);
47     calcManager = alignFrame.getViewport().getCalcManager();
48   }
49
50   @AfterMethod(alwaysRun = true)
51   public void tearDown()
52   {
53     calcManager.shutdown();
54   }
55
56   // Running workers
57
58   @Test(groups = "Functional")
59   public void testStartRegisteredWorker() throws InterruptedException
60   {
61     var sentinel = new Object();
62     var job = CompletableFuture.completedFuture(sentinel);
63     var worker = new AlignCalcWorkerMock(job);
64     calcManager.registerWorker(worker);
65     Thread.sleep(10);
66     assertSame(worker.getLastResult(), sentinel);
67   }
68
69   @Test(groups = "Functional")
70   public void testIsWorking() throws InterruptedException
71   {
72     var job = new CompletableFuture<Object>();
73     var worker = new AlignCalcWorkerMock(job);
74     calcManager.registerWorker(worker);
75     assertTrue(calcManager.isWorking(worker));
76     assertTrue(calcManager.isWorking());
77     job.complete(null);
78     Thread.sleep(10);
79     assertFalse(calcManager.isWorking(worker));
80     assertFalse(calcManager.isWorking());
81   }
82
83   @Test(groups = "Functional")
84   public void testIsWorkingWithAnnotation() throws InterruptedException
85   {
86     var job = new CompletableFuture<Void>();
87     var worker1 = new AlignCalcWorkerMock(job);
88     var annot = worker1.annotation = newAlignmentAnnotation();
89     var otherAnnot = newAlignmentAnnotation();
90     calcManager.registerWorker(worker1);
91     assertTrue(calcManager.isWorkingWithAnnotation(annot));
92     assertFalse(calcManager.isWorkingWithAnnotation(otherAnnot));
93     job.complete(null);
94     Thread.sleep(10);
95     assertFalse(calcManager.isWorkingWithAnnotation(annot));
96   }
97
98   @Test(groups = "Functional")
99   public void testRestartCompletedWorkers() throws Throwable
100   {
101     var sentinel1 = new Object();
102     var sentinel2 = new Object();
103     var job = CompletableFuture.completedFuture(sentinel1);
104     var worker = new AlignCalcWorkerMock(job);
105     calcManager.registerWorker(worker);
106     Thread.sleep(10);
107     assertSame(worker.getLastResult(), sentinel1);
108     job.obtrudeValue(sentinel2);
109     calcManager.restartWorkers();
110     Thread.sleep(10);
111     assertSame(worker.getLastResult(), sentinel2);
112   }
113
114   @Test(groups = "Functional")
115   public void testRestartCancelsWorkers() throws Throwable
116   {
117     var job = new CompletableFuture<Object>();
118     var worker = new AlignCalcWorkerMock(job);
119     var sentinel = new Object();
120     calcManager.registerWorker(worker);
121     Thread.sleep(10);
122     calcManager.restartWorkers();
123     Thread.sleep(10);
124     assertTrue(worker.wasCancelled());
125     job.obtrudeValue(sentinel);
126     Thread.sleep(10);
127     assertSame(worker.getLastResult(), sentinel);
128   }
129
130   // Disabling workers
131
132   @Test(groups = "Functional")
133   public void testDisableWorker()
134   {
135     var worker = new AlignCalcWorkerMock(null);
136     calcManager.registerWorker(worker);
137     calcManager.disableWorker(worker);
138     assertTrue(calcManager.isDisabled(worker));
139     calcManager.enableWorker(worker);
140     assertFalse(calcManager.isDisabled(worker));
141   }
142
143   @Test(groups = "Functional")
144   public void testRestartDisabledWorker() throws InterruptedException
145   {
146     var worker = new AlignCalcWorkerMock(null);
147     calcManager.registerWorker(worker);
148     Thread.sleep(10);
149     assertEquals(worker.getCallCount(), 1);
150     calcManager.disableWorker(worker);
151     calcManager.restartWorkers();
152     Thread.sleep(10);
153     assertEquals(worker.getCallCount(), 1);
154     calcManager.enableWorker(worker);
155     calcManager.restartWorkers();
156     Thread.sleep(10);
157     assertEquals(worker.getCallCount(), 2);
158   }
159
160   // Canceling workers
161
162   @Test(groups = "Functional")
163   public void testCancelWorker() throws InterruptedException
164   {
165     var worker = new AlignCalcWorkerMock(new CompletableFuture<>());
166     calcManager.registerWorker(worker);
167     Thread.sleep(10);
168     calcManager.cancelWorker(worker);
169     Thread.sleep(10);
170     assertTrue(worker.wasCancelled());
171   }
172   
173   // One-shot workers
174   
175   @Test(groups = "Functional")
176   public void testStartOneShotWorker() throws InterruptedException
177   {
178     var job = CompletableFuture.completedFuture("result");
179     var worker = new AlignCalcWorkerMock(job);
180     calcManager.startWorker(worker);
181     Thread.sleep(10);
182     assertEquals(worker.getLastResult(), "result");
183   }
184   
185   @Test(groups = "Functional")
186   public void testCancelOneShotWorker() throws InterruptedException
187   {
188     var worker = new AlignCalcWorkerMock(new CompletableFuture<>());
189     calcManager.startWorker(worker);
190     Thread.sleep(10);
191     calcManager.cancelWorker(worker);
192     Thread.sleep(10);
193     assertTrue(worker.wasCancelled());
194   }
195   
196   @Test(groups = "Functional")
197   public void restartOneShotWorker() throws InterruptedException
198   {
199     var job = CompletableFuture.completedFuture("result1");
200     var worker = new AlignCalcWorkerMock(job);
201     calcManager.startWorker(worker);
202     Thread.sleep(10);
203     job.obtrudeValue("result2");
204     calcManager.restartWorkers();
205     Thread.sleep(10);
206     
207   }
208   
209
210   // Retrieving workers
211
212   @Test(groups = "Functional")
213   public void testGetWorkersOfClass() throws InterruptedException
214   {
215     var worker1 = new AlignCalcWorkerMock(null);
216     var worker2 = new AlignCalcWorkerMock(null);
217     var worker3 = new AlignCalcWorkerMock(null) {};
218     calcManager.registerWorker(worker1);
219     calcManager.registerWorker(worker2);
220     calcManager.registerWorker(worker3);
221     final var workers = calcManager
222         .getWorkersOfClass(AlignCalcWorkerMock.class);
223     assertTrue(workers.contains(worker1) && workers.contains(worker2));
224     assertFalse(workers.contains(worker3));
225   }
226
227   // Removing workers
228
229   @Test(groups = "Functional")
230   public void testRemoveWorker()
231   {
232     var worker = new AlignCalcWorkerMock(null);
233     calcManager.registerWorker(worker);
234     calcManager.removeWorker(worker);
235     assertFalse(calcManager.getWorkers().contains(worker));
236   }
237
238   @Test(groups = "Functional")
239   public void testRemoveWorkersOfClass()
240   {
241     var worker1 = new AlignCalcWorkerMock(null);
242     var worker2 = new AlignCalcWorkerMock(null);
243     var worker3 = new AlignCalcWorkerMock(null) {};
244     calcManager.registerWorker(worker1);
245     calcManager.registerWorker(worker2);
246     calcManager.registerWorker(worker3);
247     calcManager.removeWorkersOfClass(worker1.getClass());
248     assertFalse(calcManager.getWorkers().contains(worker1)
249         || calcManager.getWorkers().contains(worker2));
250     assertTrue(calcManager.getWorkers().contains(worker3));
251   }
252
253   @Test(groups = "Functional")
254   public void testRemoveWorkersForAnnotation()
255   {
256     var worker1 = new AlignCalcWorkerMock(null);
257     var worker2 = new AlignCalcWorkerMock(null);
258     var annot = worker1.annotation = newAlignmentAnnotation();
259     calcManager.registerWorker(worker1);
260     calcManager.registerWorker(worker2);
261     calcManager.removeWorkerForAnnotation(annot);
262     var workers = calcManager.getWorkers();
263     assertFalse(workers.contains(worker1));
264     assertTrue(workers.contains(worker2));
265   }
266
267   @Test(groups = "Functional")
268   public void testRemoveNonRemovableWorker()
269   {
270     var worker = new AlignCalcWorkerMock(null);
271     worker.deletable = false;
272     calcManager.registerWorker(worker);
273     calcManager.removeWorker(worker);
274     assertTrue(calcManager.getWorkers().contains(worker));
275   }
276
277   @Test(groups = "Functional")
278   public void testRemoveNonRemovableWorkersOfClass()
279   {
280     var worker1 = new AlignCalcWorkerMock(null);
281     var worker2 = new AlignCalcWorkerMock(null);
282     worker2.deletable = false;
283     calcManager.registerWorker(worker1);
284     calcManager.registerWorker(worker2);
285     calcManager.removeWorkersOfClass(worker1.getClass());
286     var workers = calcManager.getWorkers();
287     assertFalse(workers.contains(worker1));
288     assertTrue(workers.contains(worker2));
289   }
290
291   private int annotationCount = 0;
292
293   private AlignmentAnnotation newAlignmentAnnotation()
294   {
295     return new AlignmentAnnotation("Ann" + annotationCount++, "description",
296         new Annotation[] {});
297   }
298 }
299
300 class AlignCalcWorkerMock implements AlignCalcWorkerI
301 {
302   AlignmentAnnotation annotation = null;
303   Future<?> job;
304   ArrayList<Object> values = new ArrayList<>();
305   int callCount = 0;
306   boolean deletable = true;
307
308   AlignCalcWorkerMock(Future<?> job)
309   {
310     this.job = job;
311   }
312
313   public Object getLastResult()
314   {
315     return values.isEmpty() ? null : values.get(values.size() - 1);
316   }
317
318   public Throwable getException()
319   {
320     var result = getLastResult();
321     return (result instanceof Throwable) ? (Throwable) result : null;
322   }
323   
324   public int getCallCount() {
325     return callCount;
326   }
327
328   public boolean wasCancelled()
329   {
330     return getException() instanceof InterruptedException;
331   }
332
333   @Override
334   public boolean involves(AlignmentAnnotation annot)
335   {
336     if (annotation == null)
337       return false;
338     else
339       return annot == annotation;
340   }
341
342   @Override
343   public void updateAnnotation()
344   {
345   }
346
347   @Override
348   public void removeAnnotation()
349   {
350   }
351
352   @Override
353   public void run() throws Throwable
354   {
355     callCount++;
356     if (job != null)
357     {
358       try
359       {
360         values.add(job.get());
361       } 
362       catch (InterruptedException e) { values.add(e); }
363       catch (CancellationException e) {
364         values.add(new InterruptedException());
365       } catch (ExecutionException e)
366       {
367         values.add(e.getCause());
368       }
369     }
370   }
371
372   @Override
373   public boolean isDeletable()
374   {
375     return deletable;
376   }
377 }