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-----")
102 @Test(dataProvider = "multipleSequencesUnalignedAndAligned")
103 public void submitSequences_verifySequenceNamesUniquified(
104 Alignment unaligned, Alignment aligned)
107 var viewport = new AlignViewport(unaligned);
108 when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
109 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
110 actionBuilder.submitGaps(false);
111 performAction(viewport, actionBuilder.build());
112 ArgumentCaptor<List<SequenceI>> argument = ArgumentCaptor.forClass(List.class);
113 verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty()));
114 assertThat(argument.getValue(),
115 contains(hasProperty("name", is("Sequence0")),
116 hasProperty("name", is("Sequence1")),
117 hasProperty("name", is("Sequence2"))));
120 @Test(dataProvider = "multipleSequencesUnalignedAndAligned")
121 public void submitSequences_submitGapsOff_verifySequencesSubmittedWithoutGaps(Alignment unaligned, Alignment aligned)
124 var viewport = new AlignViewport(unaligned);
125 actionBuilder.submitGaps(false);
126 when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
127 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
128 performAction(viewport, actionBuilder.build());
129 ArgumentCaptor<List<SequenceI>> argument = ArgumentCaptor.forClass(List.class);
130 verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty()));
131 assertThat(argument.getValue(),
133 matchesSequence("ASTVLITOPDCMMQEGGST"),
134 matchesSequence("ASCGLITOMMQEGGST"),
135 matchesSequence("ASTVLOPDTMMQEL")));
138 @Test(dataProvider = "multipleSequencesUnalignedAndAligned")
139 public void submitSequences_submitGapsOn_verifySequencesSubmittedWithGaps(
140 Alignment unaligned, Alignment aligned)
143 var viewport = new AlignViewport(unaligned);
144 actionBuilder.submitGaps(true);
145 when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
146 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
147 performAction(viewport, actionBuilder.build());
148 ArgumentCaptor<List<SequenceI>> argument = ArgumentCaptor.forClass(List.class);
149 verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty()));
150 assertThat(argument.getValue(),
152 matchesSequence("----ASTVLITOPDCMMQEGGST-"),
153 matchesSequence("-ASCGLITO------MMQEGGST-"),
154 matchesSequence("AS--TVL--OPDTMMQEL------")));
157 @Test(dataProvider = "multipleSequencesUnalignedAndAligned")
158 public void retrieveResult_verifySequencesAligned(
159 Alignment unaligned, Alignment aligned)
162 var viewport = new AlignViewport(unaligned);
163 actionBuilder.submitGaps(false);
164 when(mockClient.getAlignment(jobRef)).thenReturn(aligned);
165 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
166 var mockListener = performAction(viewport, actionBuilder.build());
167 var argument = ArgumentCaptor.forClass(AlignmentResult.class);
168 verify(mockListener).taskCompleted(any(), argument.capture());
169 var alignmentResult = argument.getValue().getAlignment();
170 assertThat(alignmentResult, hasProperty("sequences", contains(
171 matchesSequence("ASTV-LITOPDCMMQEGGST----"),
172 matchesSequence("ASC-GLITO---MMQEGGST----"),
173 matchesSequence("ASTV-L--OPDTMMQE--L-----"))));
176 protected static Matcher<SequenceI> matchesSequence(String sequence)
178 return new TypeSafeMatcher<SequenceI>()
181 public boolean matchesSafely(SequenceI obj)
183 if (!(obj instanceof SequenceI))
185 var seq = (SequenceI) obj;
186 return seq.getSequenceAsString().equals(sequence);
190 public void describeTo(Description description)
192 description.appendText("a sequence ").appendValue(sequence);
196 public void describeMismatchSafely(SequenceI item, Description description)
198 description.appendText("was ").appendValue(item.getSequenceAsString());
203 protected TaskEventListener<AlignmentResult> performAction(
204 AlignmentViewport viewport, AlignmentAction action)
207 TaskEventListener<AlignmentResult> listener = mock(TaskEventListener.class);
208 var latch = new CountDownLatch(1);
209 doAnswer(invocation -> {
213 .when(listener).taskCompleted(any(), any());
214 var executor = PollingTaskExecutor.fromPool(viewport.getServiceExecutor());
215 var task = action.createTask(viewport, List.of(), Credentials.empty());
216 task.addTaskEventListener(listener);
217 var cancellable = executor.submit(task);
220 latch.await(100, TimeUnit.MILLISECONDS);
221 } catch (InterruptedException e)
223 cancellable.cancel(true);
229 class AlignmentActionListenerNotifiedTest extends AlignmentActionTest
231 private AlignViewport viewport;
234 public void setupViewport()
236 viewport = new AlignViewport(new Alignment(new SequenceI[] {
237 new Sequence("Seq 1", "----ASTVLITOPDCMMQEGGST-"),
238 new Sequence("Seq 2", "-ASCGLITO------MMQEGGST-"),
239 new Sequence("Seq 3", "AS--TVL--OPDTMMQEL------")
244 public JobStatus[] jobStatuses()
246 // CREATED, INVALID and READY should not be returned by the server
247 return new JobStatus[] {
254 JobStatus.SERVER_ERROR,
260 public void allJobsStarted_taskStartedCalled()
263 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
264 var mockListener = performAction(viewport, actionBuilder.build());
265 verify(mockListener).taskStarted(any(), anyList());
269 public void allJobsStarted_taskStatusChangedCalledWithReadyThenSubmitted()
272 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
273 var mockListener = performAction(viewport, actionBuilder.build());
274 var inOrder = inOrder(mockListener);
275 inOrder.verify(mockListener).taskStatusChanged(any(), eq(JobStatus.READY));
276 inOrder.verify(mockListener).taskStatusChanged(any(), eq(JobStatus.SUBMITTED));
279 @Test(dataProvider = "jobStatuses")
280 public void jobStatusChanged_taskStatusChangedCalledWithJobStatus(JobStatus status)
283 when(mockClient.getStatus(jobRef))
285 .thenReturn(JobStatus.COMPLETED);
286 var mockListener = performAction(viewport, actionBuilder.build());
287 verify(mockListener).taskStatusChanged(any(), eq(status));
290 @Test(dataProvider = "jobStatuses")
291 public void jobStatusChanged_subJobStatusChangedCalledWithJobStatus(JobStatus status)
294 when(mockClient.getStatus(jobRef))
296 .thenReturn(JobStatus.COMPLETED);
297 var mockListener = performAction(viewport, actionBuilder.build());
298 verify(mockListener).subJobStatusChanged(any(), any(), eq(status));