1 package jalview.ws2.actions.alignment;
3 import static jalview.testutils.Matchers.matchesSequenceString;
4 import static org.hamcrest.MatcherAssert.assertThat;
5 import static org.hamcrest.Matchers.contains;
6 import static org.hamcrest.Matchers.hasProperty;
7 import static org.hamcrest.Matchers.is;
8 import static org.mockito.ArgumentMatchers.any;
9 import static org.mockito.ArgumentMatchers.anyList;
10 import static org.mockito.ArgumentMatchers.eq;
11 import static org.mockito.Mockito.doAnswer;
12 import static org.mockito.Mockito.doThrow;
13 import static org.mockito.Mockito.inOrder;
14 import static org.mockito.Mockito.mock;
15 import static org.mockito.Mockito.verify;
16 import static org.mockito.Mockito.when;
18 import java.io.IOException;
20 import java.util.List;
21 import java.util.concurrent.CountDownLatch;
22 import java.util.concurrent.TimeUnit;
24 import org.mockito.ArgumentCaptor;
25 import org.testng.annotations.BeforeMethod;
26 import org.testng.annotations.DataProvider;
27 import org.testng.annotations.Test;
29 import jalview.datamodel.Alignment;
30 import jalview.datamodel.Sequence;
31 import jalview.datamodel.SequenceI;
32 import jalview.gui.AlignViewport;
33 import jalview.viewmodel.AlignmentViewport;
34 import jalview.ws.params.ParamDatastoreI;
35 import jalview.ws2.actions.PollingTaskExecutor;
36 import jalview.ws2.actions.api.TaskEventListener;
37 import jalview.ws2.api.Credentials;
38 import jalview.ws2.api.JobStatus;
39 import jalview.ws2.api.WebService;
40 import jalview.ws2.api.WebServiceJobHandle;
41 import jalview.ws2.client.api.AlignmentWebServiceClientI;
43 public class AlignmentActionTest
45 protected AlignmentWebServiceClientI mockClient;
47 protected AlignmentAction.Builder actionBuilder;
49 protected WebServiceJobHandle jobRef;
51 @BeforeMethod(alwaysRun = true)
52 public void setupMockClient() throws IOException
54 jobRef = new WebServiceJobHandle(
55 "mock", "mock", "http://example.org", "00000001");
56 mockClient = mock(AlignmentWebServiceClientI.class);
57 when(mockClient.getUrl()).thenReturn("http://example.org");
58 when(mockClient.getClientName()).thenReturn("mock");
59 when(mockClient.submit(anyList(), anyList(), any())).thenReturn(jobRef);
60 when(mockClient.getLog(jobRef)).thenReturn("");
61 when(mockClient.getErrorLog(jobRef)).thenReturn("");
62 doThrow(new UnsupportedOperationException()).when(mockClient).cancel(any());
65 @BeforeMethod(alwaysRun = true, dependsOnMethods = { "setupMockClient" })
66 public void setupActionBuilder() throws IOException
68 actionBuilder = AlignmentAction.newBuilder(mockClient);
69 actionBuilder.name("mock");
70 actionBuilder.webService(
71 WebService.<AlignmentAction> newBuilder()
72 .url(new URL("http://example.org"))
74 .category("Alignment")
76 .paramDatastore(mock(ParamDatastoreI.class))
77 .actionClass(AlignmentAction.class)
82 public Object[][] multipleSequencesUnalignedAndAligned()
84 return new Object[][] {
86 new Alignment(new SequenceI[]
88 new Sequence("Seq 1", "----ASTVLITOPDCMMQEGGST-"),
89 new Sequence("Seq 2", "-ASCGLITO------MMQEGGST-"),
90 new Sequence("Seq 3", "AS--TVL--OPDTMMQEL------")
92 new Alignment(new SequenceI[]
94 new Sequence("Sequence0", "ASTV-LITOPDCMMQEGGST----"),
95 new Sequence("Sequence1", "ASC-GLITO---MMQEGGST----"),
96 new Sequence("Sequence2", "ASTV-L--OPDTMMQE--L-----")
103 groups = { "Functional" },
104 dataProvider = "multipleSequencesUnalignedAndAligned")
105 public void submitSequences_verifySequenceNamesUniquified(
106 Alignment unaligned, Alignment aligned)
109 var viewport = new AlignViewport(unaligned);
110 when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
111 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
112 actionBuilder.submitGaps(false);
113 performAction(viewport, actionBuilder.build());
114 ArgumentCaptor<List<SequenceI>> argument = ArgumentCaptor.forClass(List.class);
115 verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty()));
116 assertThat(argument.getValue(),
117 contains(hasProperty("name", is("Sequence0")),
118 hasProperty("name", is("Sequence1")),
119 hasProperty("name", is("Sequence2"))));
123 groups = { "Functional" },
124 dataProvider = "multipleSequencesUnalignedAndAligned")
125 public void submitSequences_submitGapsOff_verifySequencesSubmittedWithoutGaps(Alignment unaligned, Alignment aligned)
128 var viewport = new AlignViewport(unaligned);
129 actionBuilder.submitGaps(false);
130 when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
131 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
132 performAction(viewport, actionBuilder.build());
133 ArgumentCaptor<List<SequenceI>> argument = ArgumentCaptor.forClass(List.class);
134 verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty()));
135 assertThat(argument.getValue(),
137 matchesSequenceString("ASTVLITOPDCMMQEGGST"),
138 matchesSequenceString("ASCGLITOMMQEGGST"),
139 matchesSequenceString("ASTVLOPDTMMQEL")));
143 groups = { "Functional" },
144 dataProvider = "multipleSequencesUnalignedAndAligned")
145 public void submitSequences_submitGapsOn_verifySequencesSubmittedWithGaps(
146 Alignment unaligned, Alignment aligned)
149 var viewport = new AlignViewport(unaligned);
150 actionBuilder.submitGaps(true);
151 when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
152 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
153 performAction(viewport, actionBuilder.build());
154 ArgumentCaptor<List<SequenceI>> argument = ArgumentCaptor.forClass(List.class);
155 verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty()));
156 assertThat(argument.getValue(),
158 matchesSequenceString("----ASTVLITOPDCMMQEGGST-"),
159 matchesSequenceString("-ASCGLITO------MMQEGGST-"),
160 matchesSequenceString("AS--TVL--OPDTMMQEL------")));
164 groups = { "Functional" },
165 dataProvider = "multipleSequencesUnalignedAndAligned")
166 public void retrieveResult_verifySequencesAligned(
167 Alignment unaligned, Alignment aligned)
170 var viewport = new AlignViewport(unaligned);
171 actionBuilder.submitGaps(false);
172 when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
173 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
174 var mockListener = performAction(viewport, actionBuilder.build());
175 var argument = ArgumentCaptor.forClass(AlignmentResult.class);
176 verify(mockListener).taskCompleted(any(), argument.capture());
177 var alignmentResult = argument.getValue().getAlignment();
178 assertThat(alignmentResult, hasProperty("sequences", contains(
179 matchesSequenceString("ASTV-LITOPDCMMQEGGST----"),
180 matchesSequenceString("ASC-GLITO---MMQEGGST----"),
181 matchesSequenceString("ASTV-L--OPDTMMQE--L-----"))));
184 protected TaskEventListener<AlignmentResult> performAction(
185 AlignmentViewport viewport, AlignmentAction action)
188 TaskEventListener<AlignmentResult> listener = mock(TaskEventListener.class);
189 var latch = new CountDownLatch(1);
190 doAnswer(invocation -> {
194 .when(listener).taskCompleted(any(), any());
195 var executor = PollingTaskExecutor.fromPool(viewport.getServiceExecutor());
196 var task = action.createTask(viewport, List.of(), Credentials.empty());
197 task.addTaskEventListener(listener);
198 var cancellable = executor.submit(task);
201 latch.await(100, TimeUnit.MILLISECONDS);
202 } catch (InterruptedException e)
204 cancellable.cancel(true);
210 class AlignmentActionListenerNotifiedTest extends AlignmentActionTest
212 private AlignViewport viewport;
214 @BeforeMethod(alwaysRun = true)
215 public void setupViewport()
217 viewport = new AlignViewport(new Alignment(new SequenceI[] {
218 new Sequence("Seq 1", "----ASTVLITOPDCMMQEGGST-"),
219 new Sequence("Seq 2", "-ASCGLITO------MMQEGGST-"),
220 new Sequence("Seq 3", "AS--TVL--OPDTMMQEL------")
225 public JobStatus[] jobStatuses()
227 // CREATED, INVALID and READY should not be returned by the server
228 return new JobStatus[] {
235 JobStatus.SERVER_ERROR,
240 @Test(groups = { "Functional" })
241 public void allJobsStarted_taskStartedCalled()
244 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
245 var mockListener = performAction(viewport, actionBuilder.build());
246 verify(mockListener).taskStarted(any(), anyList());
249 @Test(groups = { "Functional" })
250 public void allJobsStarted_taskStatusChangedCalledWithReadyThenSubmitted()
253 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
254 var mockListener = performAction(viewport, actionBuilder.build());
255 var inOrder = inOrder(mockListener);
256 inOrder.verify(mockListener).taskStatusChanged(any(), eq(JobStatus.READY));
257 inOrder.verify(mockListener).taskStatusChanged(any(), eq(JobStatus.SUBMITTED));
260 @Test(groups = { "Functional" }, dataProvider = "jobStatuses")
261 public void jobStatusChanged_taskStatusChangedCalledWithJobStatus(JobStatus status)
264 when(mockClient.getStatus(jobRef))
266 .thenReturn(JobStatus.COMPLETED);
267 var mockListener = performAction(viewport, actionBuilder.build());
268 verify(mockListener).taskStatusChanged(any(), eq(status));
271 @Test(groups = { "Functional" }, dataProvider = "jobStatuses")
272 public void jobStatusChanged_subJobStatusChangedCalledWithJobStatus(JobStatus status)
275 when(mockClient.getStatus(jobRef))
277 .thenReturn(JobStatus.COMPLETED);
278 var mockListener = performAction(viewport, actionBuilder.build());
279 verify(mockListener).subJobStatusChanged(any(), any(), eq(status));