1 package jalview.ws2.actions.alignment;
3 import java.io.IOException;
6 import java.util.concurrent.CountDownLatch;
7 import java.util.concurrent.TimeUnit;
9 import javax.help.UnsupportedOperationException;
11 import org.hamcrest.BaseMatcher;
12 import org.hamcrest.Description;
13 import org.hamcrest.Matcher;
14 import org.hamcrest.TypeSafeMatcher;
15 import org.mockito.ArgumentCaptor;
16 import org.testng.annotations.BeforeMethod;
17 import org.testng.annotations.DataProvider;
18 import org.testng.annotations.Test;
20 import jalview.datamodel.Alignment;
21 import jalview.datamodel.AlignmentI;
22 import jalview.datamodel.Sequence;
23 import jalview.datamodel.SequenceI;
24 import jalview.gui.AlignViewport;
25 import jalview.viewmodel.AlignmentViewport;
26 import jalview.ws.params.ParamDatastoreI;
27 import jalview.ws2.actions.PollingTaskExecutor;
28 import jalview.ws2.actions.api.JobI;
29 import jalview.ws2.actions.api.TaskEventListener;
30 import jalview.ws2.api.Credentials;
31 import jalview.ws2.api.JobStatus;
32 import jalview.ws2.api.WebService;
33 import jalview.ws2.api.WebServiceJobHandle;
34 import jalview.ws2.client.api.AlignmentWebServiceClientI;
36 import org.mockito.hamcrest.MockitoHamcrest;
37 import org.mockito.internal.hamcrest.HamcrestArgumentMatcher;
39 import static org.mockito.Mockito.*;
40 import static org.hamcrest.MatcherAssert.assertThat;
41 import static org.hamcrest.Matchers.*;
43 public class AlignmentActionTest
45 protected AlignmentWebServiceClientI mockClient;
47 protected AlignmentAction.Builder actionBuilder;
49 protected WebServiceJobHandle jobRef;
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(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 matchesSequence("ASTVLITOPDCMMQEGGST"),
138 matchesSequence("ASCGLITOMMQEGGST"),
139 matchesSequence("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 matchesSequence("----ASTVLITOPDCMMQEGGST-"),
159 matchesSequence("-ASCGLITO------MMQEGGST-"),
160 matchesSequence("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 matchesSequence("ASTV-LITOPDCMMQEGGST----"),
180 matchesSequence("ASC-GLITO---MMQEGGST----"),
181 matchesSequence("ASTV-L--OPDTMMQE--L-----"))));
184 protected static Matcher<SequenceI> matchesSequence(String sequence)
186 return new TypeSafeMatcher<SequenceI>()
189 public boolean matchesSafely(SequenceI obj)
191 if (!(obj instanceof SequenceI))
193 var seq = (SequenceI) obj;
194 return seq.getSequenceAsString().equals(sequence);
198 public void describeTo(Description description)
200 description.appendText("a sequence ").appendValue(sequence);
204 public void describeMismatchSafely(SequenceI item, Description description)
206 description.appendText("was ").appendValue(item.getSequenceAsString());
211 protected TaskEventListener<AlignmentResult> performAction(
212 AlignmentViewport viewport, AlignmentAction action)
215 TaskEventListener<AlignmentResult> listener = mock(TaskEventListener.class);
216 var latch = new CountDownLatch(1);
217 doAnswer(invocation -> {
221 .when(listener).taskCompleted(any(), any());
222 var executor = PollingTaskExecutor.fromPool(viewport.getServiceExecutor());
223 var task = action.createTask(viewport, List.of(), Credentials.empty());
224 task.addTaskEventListener(listener);
225 var cancellable = executor.submit(task);
228 latch.await(100, TimeUnit.MILLISECONDS);
229 } catch (InterruptedException e)
231 cancellable.cancel(true);
237 class AlignmentActionListenerNotifiedTest extends AlignmentActionTest
239 private AlignViewport viewport;
242 public void setupViewport()
244 viewport = new AlignViewport(new Alignment(new SequenceI[] {
245 new Sequence("Seq 1", "----ASTVLITOPDCMMQEGGST-"),
246 new Sequence("Seq 2", "-ASCGLITO------MMQEGGST-"),
247 new Sequence("Seq 3", "AS--TVL--OPDTMMQEL------")
252 public JobStatus[] jobStatuses()
254 // CREATED, INVALID and READY should not be returned by the server
255 return new JobStatus[] {
262 JobStatus.SERVER_ERROR,
267 @Test(groups = { "Functional" })
268 public void allJobsStarted_taskStartedCalled()
271 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
272 var mockListener = performAction(viewport, actionBuilder.build());
273 verify(mockListener).taskStarted(any(), anyList());
276 @Test(groups = { "Functional" })
277 public void allJobsStarted_taskStatusChangedCalledWithReadyThenSubmitted()
280 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
281 var mockListener = performAction(viewport, actionBuilder.build());
282 var inOrder = inOrder(mockListener);
283 inOrder.verify(mockListener).taskStatusChanged(any(), eq(JobStatus.READY));
284 inOrder.verify(mockListener).taskStatusChanged(any(), eq(JobStatus.SUBMITTED));
287 @Test(groups = { "Functional" }, dataProvider = "jobStatuses")
288 public void jobStatusChanged_taskStatusChangedCalledWithJobStatus(JobStatus status)
291 when(mockClient.getStatus(jobRef))
293 .thenReturn(JobStatus.COMPLETED);
294 var mockListener = performAction(viewport, actionBuilder.build());
295 verify(mockListener).taskStatusChanged(any(), eq(status));
298 @Test(groups = { "Functional" }, dataProvider = "jobStatuses")
299 public void jobStatusChanged_subJobStatusChangedCalledWithJobStatus(JobStatus status)
302 when(mockClient.getStatus(jobRef))
304 .thenReturn(JobStatus.COMPLETED);
305 var mockListener = performAction(viewport, actionBuilder.build());
306 verify(mockListener).subJobStatusChanged(any(), any(), eq(status));