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 javax.help.UnsupportedOperationException;
26 import org.mockito.ArgumentCaptor;
27 import org.testng.annotations.BeforeMethod;
28 import org.testng.annotations.DataProvider;
29 import org.testng.annotations.Test;
31 import jalview.datamodel.Alignment;
32 import jalview.datamodel.Sequence;
33 import jalview.datamodel.SequenceI;
34 import jalview.gui.AlignViewport;
35 import jalview.viewmodel.AlignmentViewport;
36 import jalview.ws.params.ParamDatastoreI;
37 import jalview.ws2.actions.PollingTaskExecutor;
38 import jalview.ws2.actions.api.TaskEventListener;
39 import jalview.ws2.api.Credentials;
40 import jalview.ws2.api.JobStatus;
41 import jalview.ws2.api.WebService;
42 import jalview.ws2.api.WebServiceJobHandle;
43 import jalview.ws2.client.api.AlignmentWebServiceClientI;
45 public class AlignmentActionTest
47 protected AlignmentWebServiceClientI mockClient;
49 protected AlignmentAction.Builder actionBuilder;
51 protected WebServiceJobHandle jobRef;
53 @BeforeMethod(alwaysRun = true)
54 public void setupMockClient() throws IOException
56 jobRef = new WebServiceJobHandle(
57 "mock", "mock", "http://example.org", "00000001");
58 mockClient = mock(AlignmentWebServiceClientI.class);
59 when(mockClient.getUrl()).thenReturn("http://example.org");
60 when(mockClient.getClientName()).thenReturn("mock");
61 when(mockClient.submit(anyList(), anyList(), any())).thenReturn(jobRef);
62 when(mockClient.getLog(jobRef)).thenReturn("");
63 when(mockClient.getErrorLog(jobRef)).thenReturn("");
64 doThrow(new UnsupportedOperationException()).when(mockClient).cancel(any());
67 @BeforeMethod(alwaysRun = true, dependsOnMethods = { "setupMockClient" })
68 public void setupActionBuilder() throws IOException
70 actionBuilder = AlignmentAction.newBuilder(mockClient);
71 actionBuilder.name("mock");
72 actionBuilder.webService(
73 WebService.<AlignmentAction> newBuilder()
74 .url(new URL("http://example.org"))
76 .category("Alignment")
78 .paramDatastore(mock(ParamDatastoreI.class))
79 .actionClass(AlignmentAction.class)
84 public Object[][] multipleSequencesUnalignedAndAligned()
86 return new Object[][] {
88 new Alignment(new SequenceI[]
90 new Sequence("Seq 1", "----ASTVLITOPDCMMQEGGST-"),
91 new Sequence("Seq 2", "-ASCGLITO------MMQEGGST-"),
92 new Sequence("Seq 3", "AS--TVL--OPDTMMQEL------")
94 new Alignment(new SequenceI[]
96 new Sequence("Sequence0", "ASTV-LITOPDCMMQEGGST----"),
97 new Sequence("Sequence1", "ASC-GLITO---MMQEGGST----"),
98 new Sequence("Sequence2", "ASTV-L--OPDTMMQE--L-----")
105 groups = { "Functional" },
106 dataProvider = "multipleSequencesUnalignedAndAligned")
107 public void submitSequences_verifySequenceNamesUniquified(
108 Alignment unaligned, Alignment aligned)
111 var viewport = new AlignViewport(unaligned);
112 when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
113 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
114 actionBuilder.submitGaps(false);
115 performAction(viewport, actionBuilder.build());
116 ArgumentCaptor<List<SequenceI>> argument = ArgumentCaptor.forClass(List.class);
117 verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty()));
118 assertThat(argument.getValue(),
119 contains(hasProperty("name", is("Sequence0")),
120 hasProperty("name", is("Sequence1")),
121 hasProperty("name", is("Sequence2"))));
125 groups = { "Functional" },
126 dataProvider = "multipleSequencesUnalignedAndAligned")
127 public void submitSequences_submitGapsOff_verifySequencesSubmittedWithoutGaps(Alignment unaligned, Alignment aligned)
130 var viewport = new AlignViewport(unaligned);
131 actionBuilder.submitGaps(false);
132 when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
133 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
134 performAction(viewport, actionBuilder.build());
135 ArgumentCaptor<List<SequenceI>> argument = ArgumentCaptor.forClass(List.class);
136 verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty()));
137 assertThat(argument.getValue(),
139 matchesSequenceString("ASTVLITOPDCMMQEGGST"),
140 matchesSequenceString("ASCGLITOMMQEGGST"),
141 matchesSequenceString("ASTVLOPDTMMQEL")));
145 groups = { "Functional" },
146 dataProvider = "multipleSequencesUnalignedAndAligned")
147 public void submitSequences_submitGapsOn_verifySequencesSubmittedWithGaps(
148 Alignment unaligned, Alignment aligned)
151 var viewport = new AlignViewport(unaligned);
152 actionBuilder.submitGaps(true);
153 when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
154 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
155 performAction(viewport, actionBuilder.build());
156 ArgumentCaptor<List<SequenceI>> argument = ArgumentCaptor.forClass(List.class);
157 verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty()));
158 assertThat(argument.getValue(),
160 matchesSequenceString("----ASTVLITOPDCMMQEGGST-"),
161 matchesSequenceString("-ASCGLITO------MMQEGGST-"),
162 matchesSequenceString("AS--TVL--OPDTMMQEL------")));
166 groups = { "Functional" },
167 dataProvider = "multipleSequencesUnalignedAndAligned")
168 public void retrieveResult_verifySequencesAligned(
169 Alignment unaligned, Alignment aligned)
172 var viewport = new AlignViewport(unaligned);
173 actionBuilder.submitGaps(false);
174 when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
175 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
176 var mockListener = performAction(viewport, actionBuilder.build());
177 var argument = ArgumentCaptor.forClass(AlignmentResult.class);
178 verify(mockListener).taskCompleted(any(), argument.capture());
179 var alignmentResult = argument.getValue().getAlignment();
180 assertThat(alignmentResult, hasProperty("sequences", contains(
181 matchesSequenceString("ASTV-LITOPDCMMQEGGST----"),
182 matchesSequenceString("ASC-GLITO---MMQEGGST----"),
183 matchesSequenceString("ASTV-L--OPDTMMQE--L-----"))));
186 protected TaskEventListener<AlignmentResult> performAction(
187 AlignmentViewport viewport, AlignmentAction action)
190 TaskEventListener<AlignmentResult> listener = mock(TaskEventListener.class);
191 var latch = new CountDownLatch(1);
192 doAnswer(invocation -> {
196 .when(listener).taskCompleted(any(), any());
197 var executor = PollingTaskExecutor.fromPool(viewport.getServiceExecutor());
198 var task = action.createTask(viewport, List.of(), Credentials.empty());
199 task.addTaskEventListener(listener);
200 var cancellable = executor.submit(task);
203 latch.await(100, TimeUnit.MILLISECONDS);
204 } catch (InterruptedException e)
206 cancellable.cancel(true);
212 class AlignmentActionListenerNotifiedTest extends AlignmentActionTest
214 private AlignViewport viewport;
216 @BeforeMethod(alwaysRun = true)
217 public void setupViewport()
219 viewport = new AlignViewport(new Alignment(new SequenceI[] {
220 new Sequence("Seq 1", "----ASTVLITOPDCMMQEGGST-"),
221 new Sequence("Seq 2", "-ASCGLITO------MMQEGGST-"),
222 new Sequence("Seq 3", "AS--TVL--OPDTMMQEL------")
227 public JobStatus[] jobStatuses()
229 // CREATED, INVALID and READY should not be returned by the server
230 return new JobStatus[] {
237 JobStatus.SERVER_ERROR,
242 @Test(groups = { "Functional" })
243 public void allJobsStarted_taskStartedCalled()
246 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
247 var mockListener = performAction(viewport, actionBuilder.build());
248 verify(mockListener).taskStarted(any(), anyList());
251 @Test(groups = { "Functional" })
252 public void allJobsStarted_taskStatusChangedCalledWithReadyThenSubmitted()
255 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
256 var mockListener = performAction(viewport, actionBuilder.build());
257 var inOrder = inOrder(mockListener);
258 inOrder.verify(mockListener).taskStatusChanged(any(), eq(JobStatus.READY));
259 inOrder.verify(mockListener).taskStatusChanged(any(), eq(JobStatus.SUBMITTED));
262 @Test(groups = { "Functional" }, dataProvider = "jobStatuses")
263 public void jobStatusChanged_taskStatusChangedCalledWithJobStatus(JobStatus status)
266 when(mockClient.getStatus(jobRef))
268 .thenReturn(JobStatus.COMPLETED);
269 var mockListener = performAction(viewport, actionBuilder.build());
270 verify(mockListener).taskStatusChanged(any(), eq(status));
273 @Test(groups = { "Functional" }, dataProvider = "jobStatuses")
274 public void jobStatusChanged_subJobStatusChangedCalledWithJobStatus(JobStatus status)
277 when(mockClient.getStatus(jobRef))
279 .thenReturn(JobStatus.COMPLETED);
280 var mockListener = performAction(viewport, actionBuilder.build());
281 verify(mockListener).subJobStatusChanged(any(), any(), eq(status));