1 package jalview.ws2.actions.alignment;
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;
17 import java.io.IOException;
19 import java.util.List;
20 import java.util.concurrent.CountDownLatch;
21 import java.util.concurrent.TimeUnit;
23 import javax.help.UnsupportedOperationException;
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;
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;
46 public class AlignmentActionTest
48 protected AlignmentWebServiceClientI mockClient;
50 protected AlignmentAction.Builder actionBuilder;
52 protected WebServiceJobHandle jobRef;
54 @BeforeMethod(alwaysRun = true)
55 public void setupMockClient() throws IOException
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());
68 @BeforeMethod(alwaysRun = true, dependsOnMethods = { "setupMockClient" })
69 public void setupActionBuilder() throws IOException
71 actionBuilder = AlignmentAction.newBuilder(mockClient);
72 actionBuilder.name("mock");
73 actionBuilder.webService(
74 WebService.<AlignmentAction> newBuilder()
75 .url(new URL("http://example.org"))
77 .category("Alignment")
79 .paramDatastore(mock(ParamDatastoreI.class))
80 .actionClass(AlignmentAction.class)
85 public Object[][] multipleSequencesUnalignedAndAligned()
87 return new Object[][] {
89 new Alignment(new SequenceI[]
91 new Sequence("Seq 1", "----ASTVLITOPDCMMQEGGST-"),
92 new Sequence("Seq 2", "-ASCGLITO------MMQEGGST-"),
93 new Sequence("Seq 3", "AS--TVL--OPDTMMQEL------")
95 new Alignment(new SequenceI[]
97 new Sequence("Sequence0", "ASTV-LITOPDCMMQEGGST----"),
98 new Sequence("Sequence1", "ASC-GLITO---MMQEGGST----"),
99 new Sequence("Sequence2", "ASTV-L--OPDTMMQE--L-----")
106 groups = { "Functional" },
107 dataProvider = "multipleSequencesUnalignedAndAligned")
108 public void submitSequences_verifySequenceNamesUniquified(
109 Alignment unaligned, Alignment aligned)
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"))));
126 groups = { "Functional" },
127 dataProvider = "multipleSequencesUnalignedAndAligned")
128 public void submitSequences_submitGapsOff_verifySequencesSubmittedWithoutGaps(Alignment unaligned, Alignment aligned)
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(),
140 matchesSequence("ASTVLITOPDCMMQEGGST"),
141 matchesSequence("ASCGLITOMMQEGGST"),
142 matchesSequence("ASTVLOPDTMMQEL")));
146 groups = { "Functional" },
147 dataProvider = "multipleSequencesUnalignedAndAligned")
148 public void submitSequences_submitGapsOn_verifySequencesSubmittedWithGaps(
149 Alignment unaligned, Alignment aligned)
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(),
161 matchesSequence("----ASTVLITOPDCMMQEGGST-"),
162 matchesSequence("-ASCGLITO------MMQEGGST-"),
163 matchesSequence("AS--TVL--OPDTMMQEL------")));
167 groups = { "Functional" },
168 dataProvider = "multipleSequencesUnalignedAndAligned")
169 public void retrieveResult_verifySequencesAligned(
170 Alignment unaligned, Alignment aligned)
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-----"))));
187 protected static Matcher<SequenceI> matchesSequence(String sequence)
189 return new TypeSafeMatcher<SequenceI>()
192 public boolean matchesSafely(SequenceI obj)
194 if (!(obj instanceof SequenceI))
196 var seq = (SequenceI) obj;
197 return seq.getSequenceAsString().equals(sequence);
201 public void describeTo(Description description)
203 description.appendText("a sequence ").appendValue(sequence);
207 public void describeMismatchSafely(SequenceI item, Description description)
209 description.appendText("was ").appendValue(item.getSequenceAsString());
214 protected TaskEventListener<AlignmentResult> performAction(
215 AlignmentViewport viewport, AlignmentAction action)
218 TaskEventListener<AlignmentResult> listener = mock(TaskEventListener.class);
219 var latch = new CountDownLatch(1);
220 doAnswer(invocation -> {
224 .when(listener).taskCompleted(any(), any());
225 action.perform(viewport, List.of(), Credentials.empty(), listener);
228 latch.await(100, TimeUnit.MILLISECONDS);
229 } catch (InterruptedException e)
236 class AlignmentActionListenerNotifiedTest extends AlignmentActionTest
238 private AlignViewport viewport;
240 @BeforeMethod(alwaysRun = true)
241 public void setupViewport()
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------")
251 public JobStatus[] jobStatuses()
253 // CREATED, INVALID and READY should not be returned by the server
254 return new JobStatus[] {
261 JobStatus.SERVER_ERROR,
266 @Test(groups = { "Functional" })
267 public void allJobsStarted_taskStartedCalled()
270 when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED);
271 var mockListener = performAction(viewport, actionBuilder.build());
272 verify(mockListener).taskStarted(any(), anyList());
275 @Test(groups = { "Functional" })
276 public void allJobsStarted_taskStatusChangedCalledWithReadyThenSubmitted()
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));
286 @Test(groups = { "Functional" }, dataProvider = "jobStatuses")
287 public void jobStatusChanged_taskStatusChangedCalledWithJobStatus(JobStatus status)
290 when(mockClient.getStatus(jobRef))
292 .thenReturn(JobStatus.COMPLETED);
293 var mockListener = performAction(viewport, actionBuilder.build());
294 verify(mockListener).taskStatusChanged(any(), eq(status));
297 @Test(groups = { "Functional" }, dataProvider = "jobStatuses")
298 public void jobStatusChanged_subJobStatusChangedCalledWithJobStatus(JobStatus status)
301 when(mockClient.getStatus(jobRef))
303 .thenReturn(JobStatus.COMPLETED);
304 var mockListener = performAction(viewport, actionBuilder.build());
305 verify(mockListener).subJobStatusChanged(any(), any(), eq(status));