JAL-4199 Set all setup methods to alwaysRun
[jalview.git] / test / jalview / ws2 / actions / alignment / AlignmentActionTest.java
1 package jalview.ws2.actions.alignment;
2
3 import static org.hamcrest.MatcherAssert.assertThat;
4 import static org.hamcrest.Matchers.contains;
5 import static org.hamcrest.Matchers.hasProperty;
6 import static org.hamcrest.Matchers.is;
7 import static org.mockito.ArgumentMatchers.any;
8 import static org.mockito.ArgumentMatchers.anyList;
9 import static org.mockito.ArgumentMatchers.eq;
10 import static org.mockito.Mockito.doAnswer;
11 import static org.mockito.Mockito.doThrow;
12 import static org.mockito.Mockito.inOrder;
13 import static org.mockito.Mockito.mock;
14 import static org.mockito.Mockito.verify;
15 import static org.mockito.Mockito.when;
16
17 import java.io.IOException;
18 import java.net.URL;
19 import java.util.List;
20 import java.util.concurrent.CountDownLatch;
21 import java.util.concurrent.TimeUnit;
22
23 import javax.help.UnsupportedOperationException;
24
25 import org.hamcrest.Description;
26 import org.hamcrest.Matcher;
27 import org.hamcrest.TypeSafeMatcher;
28 import org.mockito.ArgumentCaptor;
29 import org.testng.annotations.BeforeMethod;
30 import org.testng.annotations.DataProvider;
31 import org.testng.annotations.Test;
32
33 import jalview.datamodel.Alignment;
34 import jalview.datamodel.Sequence;
35 import jalview.datamodel.SequenceI;
36 import jalview.gui.AlignViewport;
37 import jalview.viewmodel.AlignmentViewport;
38 import jalview.ws.params.ParamDatastoreI;
39 import jalview.ws2.actions.api.TaskEventListener;
40 import jalview.ws2.api.Credentials;
41 import jalview.ws2.api.JobStatus;
42 import jalview.ws2.api.WebService;
43 import jalview.ws2.api.WebServiceJobHandle;
44 import jalview.ws2.client.api.AlignmentWebServiceClientI;
45
46 public class AlignmentActionTest
47 {
48   protected AlignmentWebServiceClientI mockClient;
49
50   protected AlignmentAction.Builder actionBuilder;
51
52   protected WebServiceJobHandle jobRef;
53
54   @BeforeMethod(alwaysRun = true)
55   public void setupMockClient() throws IOException
56   {
57     jobRef = new WebServiceJobHandle(
58         "mock", "mock", "http://example.org", "00000001");
59     mockClient = mock(AlignmentWebServiceClientI.class);
60     when(mockClient.getUrl()).thenReturn("http://example.org");
61     when(mockClient.getClientName()).thenReturn("mock");
62     when(mockClient.submit(anyList(), anyList(), any())).thenReturn(jobRef);
63     when(mockClient.getLog(jobRef)).thenReturn("");
64     when(mockClient.getErrorLog(jobRef)).thenReturn("");
65     doThrow(new UnsupportedOperationException()).when(mockClient).cancel(any());
66   }
67
68   @BeforeMethod(alwaysRun = true, dependsOnMethods = { "setupMockClient" })
69   public void setupActionBuilder() throws IOException
70   {
71     actionBuilder = AlignmentAction.newBuilder(mockClient);
72     actionBuilder.name("mock");
73     actionBuilder.webService(
74         WebService.<AlignmentAction> newBuilder()
75             .url(new URL("http://example.org"))
76             .clientName("mock")
77             .category("Alignment")
78             .name("mock")
79             .paramDatastore(mock(ParamDatastoreI.class))
80             .actionClass(AlignmentAction.class)
81             .build());
82   }
83
84   @DataProvider
85   public Object[][] multipleSequencesUnalignedAndAligned()
86   {
87     return new Object[][] {
88         {
89             new Alignment(new SequenceI[]
90             {
91                 new Sequence("Seq 1", "----ASTVLITOPDCMMQEGGST-"),
92                 new Sequence("Seq 2", "-ASCGLITO------MMQEGGST-"),
93                 new Sequence("Seq 3", "AS--TVL--OPDTMMQEL------")
94             }),
95             new Alignment(new SequenceI[]
96             {
97                 new Sequence("Sequence0", "ASTV-LITOPDCMMQEGGST----"),
98                 new Sequence("Sequence1", "ASC-GLITO---MMQEGGST----"),
99                 new Sequence("Sequence2", "ASTV-L--OPDTMMQE--L-----")
100             })
101         }
102     };
103   }
104
105   @Test(
106     groups = { "Functional" },
107     dataProvider = "multipleSequencesUnalignedAndAligned")
108   public void submitSequences_verifySequenceNamesUniquified(
109       Alignment unaligned, Alignment aligned)
110       throws IOException
111   {
112     var viewport = new AlignViewport(unaligned);
113     when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
114     when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
115     actionBuilder.submitGaps(false);
116     performAction(viewport, actionBuilder.build());
117     ArgumentCaptor<List<SequenceI>> argument = ArgumentCaptor.forClass(List.class);
118     verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty()));
119     assertThat(argument.getValue(),
120         contains(hasProperty("name", is("Sequence0")),
121             hasProperty("name", is("Sequence1")),
122             hasProperty("name", is("Sequence2"))));
123   }
124
125   @Test(
126     groups = { "Functional" },
127     dataProvider = "multipleSequencesUnalignedAndAligned")
128   public void submitSequences_submitGapsOff_verifySequencesSubmittedWithoutGaps(Alignment unaligned, Alignment aligned)
129       throws IOException
130   {
131     var viewport = new AlignViewport(unaligned);
132     actionBuilder.submitGaps(false);
133     when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
134     when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
135     performAction(viewport, actionBuilder.build());
136     ArgumentCaptor<List<SequenceI>> argument = ArgumentCaptor.forClass(List.class);
137     verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty()));
138     assertThat(argument.getValue(),
139         contains(
140             matchesSequence("ASTVLITOPDCMMQEGGST"),
141             matchesSequence("ASCGLITOMMQEGGST"),
142             matchesSequence("ASTVLOPDTMMQEL")));
143   }
144
145   @Test(
146     groups = { "Functional" },
147     dataProvider = "multipleSequencesUnalignedAndAligned")
148   public void submitSequences_submitGapsOn_verifySequencesSubmittedWithGaps(
149       Alignment unaligned, Alignment aligned)
150       throws IOException
151   {
152     var viewport = new AlignViewport(unaligned);
153     actionBuilder.submitGaps(true);
154     when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
155     when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
156     performAction(viewport, actionBuilder.build());
157     ArgumentCaptor<List<SequenceI>> argument = ArgumentCaptor.forClass(List.class);
158     verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty()));
159     assertThat(argument.getValue(),
160         contains(
161             matchesSequence("----ASTVLITOPDCMMQEGGST-"),
162             matchesSequence("-ASCGLITO------MMQEGGST-"),
163             matchesSequence("AS--TVL--OPDTMMQEL------")));
164   }
165
166   @Test(
167     groups = { "Functional" },
168     dataProvider = "multipleSequencesUnalignedAndAligned")
169   public void retrieveResult_verifySequencesAligned(
170       Alignment unaligned, Alignment aligned)
171       throws IOException
172   {
173     var viewport = new AlignViewport(unaligned);
174     actionBuilder.submitGaps(false);
175     when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
176     when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
177     var mockListener = performAction(viewport, actionBuilder.build());
178     var argument = ArgumentCaptor.forClass(AlignmentResult.class);
179     verify(mockListener).taskCompleted(any(), argument.capture());
180     var alignmentResult = argument.getValue().getAlignment();
181     assertThat(alignmentResult, hasProperty("sequences", contains(
182         matchesSequence("ASTV-LITOPDCMMQEGGST----"),
183         matchesSequence("ASC-GLITO---MMQEGGST----"),
184         matchesSequence("ASTV-L--OPDTMMQE--L-----"))));
185   }
186
187   protected static Matcher<SequenceI> matchesSequence(String sequence)
188   {
189     return new TypeSafeMatcher<SequenceI>()
190     {
191       @Override
192       public boolean matchesSafely(SequenceI obj)
193       {
194         if (!(obj instanceof SequenceI))
195           return false;
196         var seq = (SequenceI) obj;
197         return seq.getSequenceAsString().equals(sequence);
198       }
199
200       @Override
201       public void describeTo(Description description)
202       {
203         description.appendText("a sequence ").appendValue(sequence);
204       }
205
206       @Override
207       public void describeMismatchSafely(SequenceI item, Description description)
208       {
209         description.appendText("was ").appendValue(item.getSequenceAsString());
210       }
211     };
212   }
213
214   protected TaskEventListener<AlignmentResult> performAction(
215       AlignmentViewport viewport, AlignmentAction action)
216       throws IOException
217   {
218     TaskEventListener<AlignmentResult> listener = mock(TaskEventListener.class);
219     var latch = new CountDownLatch(1);
220     doAnswer(invocation -> {
221       latch.countDown();
222       return null;
223     })
224         .when(listener).taskCompleted(any(), any());
225     action.perform(viewport, List.of(), Credentials.empty(), listener);
226     try
227     {
228       latch.await(100, TimeUnit.MILLISECONDS);
229     } catch (InterruptedException e)
230     {
231     }
232     return listener;
233   }
234 }
235
236 class AlignmentActionListenerNotifiedTest extends AlignmentActionTest
237 {
238   private AlignViewport viewport;
239
240   @BeforeMethod(alwaysRun = true)
241   public void setupViewport()
242   {
243     viewport = new AlignViewport(new Alignment(new SequenceI[] {
244         new Sequence("Seq 1", "----ASTVLITOPDCMMQEGGST-"),
245         new Sequence("Seq 2", "-ASCGLITO------MMQEGGST-"),
246         new Sequence("Seq 3", "AS--TVL--OPDTMMQEL------")
247     }));
248   }
249
250   @DataProvider
251   public JobStatus[] jobStatuses()
252   {
253     // CREATED, INVALID and READY should not be returned by the server
254     return new JobStatus[] {
255         JobStatus.SUBMITTED,
256         JobStatus.QUEUED,
257         JobStatus.RUNNING,
258         JobStatus.COMPLETED,
259         JobStatus.FAILED,
260         JobStatus.CANCELLED,
261         JobStatus.SERVER_ERROR,
262         JobStatus.UNKNOWN
263     };
264   }
265
266   @Test(groups = { "Functional" })
267   public void allJobsStarted_taskStartedCalled()
268       throws IOException
269   {
270     when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
271     var mockListener = performAction(viewport, actionBuilder.build());
272     verify(mockListener).taskStarted(any(), anyList());
273   }
274
275   @Test(groups = { "Functional" })
276   public void allJobsStarted_taskStatusChangedCalledWithReadyThenSubmitted()
277       throws IOException
278   {
279     when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
280     var mockListener = performAction(viewport, actionBuilder.build());
281     var inOrder = inOrder(mockListener);
282     inOrder.verify(mockListener).taskStatusChanged(any(), eq(JobStatus.READY));
283     inOrder.verify(mockListener).taskStatusChanged(any(), eq(JobStatus.SUBMITTED));
284   }
285
286   @Test(groups = { "Functional" }, dataProvider = "jobStatuses")
287   public void jobStatusChanged_taskStatusChangedCalledWithJobStatus(JobStatus status)
288       throws IOException
289   {
290     when(mockClient.getStatus(jobRef))
291         .thenReturn(status)
292         .thenReturn(JobStatus.COMPLETED);
293     var mockListener = performAction(viewport, actionBuilder.build());
294     verify(mockListener).taskStatusChanged(any(), eq(status));
295   }
296
297   @Test(groups = { "Functional" }, dataProvider = "jobStatuses")
298   public void jobStatusChanged_subJobStatusChangedCalledWithJobStatus(JobStatus status)
299       throws IOException
300   {
301     when(mockClient.getStatus(jobRef))
302         .thenReturn(status)
303         .thenReturn(JobStatus.COMPLETED);
304     var mockListener = performAction(viewport, actionBuilder.build());
305     verify(mockListener).subJobStatusChanged(any(), any(), eq(status));
306   }
307 }