From: Mateusz Warowny Date: Thu, 22 Jun 2023 13:41:19 +0000 (+0200) Subject: JAL-4199 Introduce AlignmentAction tests X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=d349a5151f272209330b3f04b7a2e2bb6bcdadf8;p=jalview.git JAL-4199 Introduce AlignmentAction tests --- diff --git a/test/jalview/ws2/actions/alignment/AlignmentActionTest.java b/test/jalview/ws2/actions/alignment/AlignmentActionTest.java new file mode 100644 index 0000000..5586108 --- /dev/null +++ b/test/jalview/ws2/actions/alignment/AlignmentActionTest.java @@ -0,0 +1,295 @@ +package jalview.ws2.actions.alignment; + +import java.io.IOException; +import java.net.URL; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.help.UnsupportedOperationException; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; +import org.mockito.ArgumentCaptor; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceI; +import jalview.gui.AlignViewport; +import jalview.viewmodel.AlignmentViewport; +import jalview.ws.params.ParamDatastoreI; +import jalview.ws2.actions.api.JobI; +import jalview.ws2.actions.api.TaskEventListener; +import jalview.ws2.api.Credentials; +import jalview.ws2.api.JobStatus; +import jalview.ws2.api.WebService; +import jalview.ws2.api.WebServiceJobHandle; +import jalview.ws2.client.api.AlignmentWebServiceClientI; + +import org.mockito.hamcrest.MockitoHamcrest; +import org.mockito.internal.hamcrest.HamcrestArgumentMatcher; + +import static org.mockito.Mockito.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +public class AlignmentActionTest +{ + protected AlignmentWebServiceClientI mockClient; + + protected AlignmentAction.Builder actionBuilder; + + protected WebServiceJobHandle jobRef; + + @BeforeMethod + public void setupMockClient() throws IOException + { + jobRef = new WebServiceJobHandle( + "mock", "mock", "http://example.org", "00000001"); + mockClient = mock(AlignmentWebServiceClientI.class); + when(mockClient.getUrl()).thenReturn("http://example.org"); + when(mockClient.getClientName()).thenReturn("mock"); + when(mockClient.submit(anyList(), anyList(), any())).thenReturn(jobRef); + when(mockClient.getLog(jobRef)).thenReturn(""); + when(mockClient.getErrorLog(jobRef)).thenReturn(""); + doThrow(new UnsupportedOperationException()).when(mockClient).cancel(any()); + } + + @BeforeMethod(dependsOnMethods = { "setupMockClient" }) + public void setupActionBuilder() throws IOException + { + actionBuilder = AlignmentAction.newBuilder(mockClient); + actionBuilder.name("mock"); + actionBuilder.webService( + WebService. newBuilder() + .url(new URL("http://example.org")) + .clientName("mock") + .category("Alignment") + .name("mock") + .paramDatastore(mock(ParamDatastoreI.class)) + .actionClass(AlignmentAction.class) + .build()); + } + + @DataProvider + public Object[][] multipleSequencesUnalignedAndAligned() + { + return new Object[][] { + { + new Alignment(new SequenceI[] + { + new Sequence("Seq 1", "----ASTVLITOPDCMMQEGGST-"), + new Sequence("Seq 2", "-ASCGLITO------MMQEGGST-"), + new Sequence("Seq 3", "AS--TVL--OPDTMMQEL------") + }), + new Alignment(new SequenceI[] + { + new Sequence("Sequence0", "ASTV-LITOPDCMMQEGGST----"), + new Sequence("Sequence1", "ASC-GLITO---MMQEGGST----"), + new Sequence("Sequence2", "ASTV-L--OPDTMMQE--L-----") + }) + } + }; + } + + @Test(dataProvider = "multipleSequencesUnalignedAndAligned") + public void submitSequences_verifySequenceNamesUniquified( + Alignment unaligned, Alignment aligned) + throws IOException + { + var viewport = new AlignViewport(unaligned); + when(mockClient.getAlignment(jobRef)).thenReturn(aligned); + when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED); + actionBuilder.submitGaps(false); + performAction(viewport, actionBuilder.build()); + ArgumentCaptor> argument = ArgumentCaptor.forClass(List.class); + verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty())); + assertThat(argument.getValue(), + contains(hasProperty("name", is("Sequence0")), + hasProperty("name", is("Sequence1")), + hasProperty("name", is("Sequence2")))); + } + + @Test(dataProvider = "multipleSequencesUnalignedAndAligned") + public void submitSequences_submitGapsOff_verifySequencesSubmittedWithoutGaps(Alignment unaligned, Alignment aligned) + throws IOException + { + var viewport = new AlignViewport(unaligned); + actionBuilder.submitGaps(false); + when(mockClient.getAlignment(jobRef)).thenReturn(aligned); + when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED); + performAction(viewport, actionBuilder.build()); + ArgumentCaptor> argument = ArgumentCaptor.forClass(List.class); + verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty())); + assertThat(argument.getValue(), + contains( + matchesSequence("ASTVLITOPDCMMQEGGST"), + matchesSequence("ASCGLITOMMQEGGST"), + matchesSequence("ASTVLOPDTMMQEL"))); + } + + @Test(dataProvider = "multipleSequencesUnalignedAndAligned") + public void submitSequences_submitGapsOn_verifySequencesSubmittedWithGaps( + Alignment unaligned, Alignment aligned) + throws IOException + { + var viewport = new AlignViewport(unaligned); + actionBuilder.submitGaps(true); + when(mockClient.getAlignment(jobRef)).thenReturn(aligned); + when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED); + performAction(viewport, actionBuilder.build()); + ArgumentCaptor> argument = ArgumentCaptor.forClass(List.class); + verify(mockClient).submit(argument.capture(), eq(List.of()), eq(Credentials.empty())); + assertThat(argument.getValue(), + contains( + matchesSequence("----ASTVLITOPDCMMQEGGST-"), + matchesSequence("-ASCGLITO------MMQEGGST-"), + matchesSequence("AS--TVL--OPDTMMQEL------"))); + } + + @Test(dataProvider = "multipleSequencesUnalignedAndAligned") + public void retrieveResult_verifySequencesAligned( + Alignment unaligned, Alignment aligned) + throws IOException + { + var viewport = new AlignViewport(unaligned); + actionBuilder.submitGaps(false); + when(mockClient.getAlignment(jobRef)).thenReturn(aligned); + when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED); + var mockListener = performAction(viewport, actionBuilder.build()); + var argument = ArgumentCaptor.forClass(AlignmentResult.class); + verify(mockListener).taskCompleted(any(), argument.capture()); + var alignmentResult = argument.getValue().getAlignment(); + assertThat(alignmentResult, hasProperty("sequences", contains( + matchesSequence("ASTV-LITOPDCMMQEGGST----"), + matchesSequence("ASC-GLITO---MMQEGGST----"), + matchesSequence("ASTV-L--OPDTMMQE--L-----")))); + } + + protected static Matcher matchesSequence(String sequence) + { + return new TypeSafeMatcher() + { + @Override + public boolean matchesSafely(SequenceI obj) + { + if (!(obj instanceof SequenceI)) + return false; + var seq = (SequenceI) obj; + return seq.getSequenceAsString().equals(sequence); + } + + @Override + public void describeTo(Description description) + { + description.appendText("a sequence ").appendValue(sequence); + } + + @Override + public void describeMismatchSafely(SequenceI item, Description description) + { + description.appendText("was ").appendValue(item.getSequenceAsString()); + } + }; + } + + protected TaskEventListener performAction( + AlignmentViewport viewport, AlignmentAction action) + throws IOException + { + TaskEventListener listener = mock(TaskEventListener.class); + var latch = new CountDownLatch(1); + doAnswer(invocation -> { + latch.countDown(); + return null; + }) + .when(listener).taskCompleted(any(), any()); + action.perform(viewport, List.of(), Credentials.empty(), listener); + try + { + latch.await(100, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) + { + } + return listener; + } +} + +class AlignmentActionListenerNotifiedTest extends AlignmentActionTest +{ + private AlignViewport viewport; + + @BeforeMethod + public void setupViewport() + { + viewport = new AlignViewport(new Alignment(new SequenceI[] { + new Sequence("Seq 1", "----ASTVLITOPDCMMQEGGST-"), + new Sequence("Seq 2", "-ASCGLITO------MMQEGGST-"), + new Sequence("Seq 3", "AS--TVL--OPDTMMQEL------") + })); + } + + @DataProvider + public JobStatus[] jobStatuses() + { + // CREATED, INVALID and READY should not be returned by the server + return new JobStatus[] { + JobStatus.SUBMITTED, + JobStatus.QUEUED, + JobStatus.RUNNING, + JobStatus.COMPLETED, + JobStatus.FAILED, + JobStatus.CANCELLED, + JobStatus.SERVER_ERROR, + JobStatus.UNKNOWN + }; + } + + @Test + public void allJobsStarted_taskStartedCalled() + throws IOException + { + when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED); + var mockListener = performAction(viewport, actionBuilder.build()); + verify(mockListener).taskStarted(any(), anyList()); + } + + @Test + public void allJobsStarted_taskStatusChangedCalledWithReadyThenSubmitted() + throws IOException + { + when(mockClient.getStatus(jobRef)).thenReturn(JobStatus.COMPLETED); + var mockListener = performAction(viewport, actionBuilder.build()); + var inOrder = inOrder(mockListener); + inOrder.verify(mockListener).taskStatusChanged(any(), eq(JobStatus.READY)); + inOrder.verify(mockListener).taskStatusChanged(any(), eq(JobStatus.SUBMITTED)); + } + + @Test(dataProvider = "jobStatuses") + public void jobStatusChanged_taskStatusChangedCalledWithJobStatus(JobStatus status) + throws IOException + { + when(mockClient.getStatus(jobRef)) + .thenReturn(status) + .thenReturn(JobStatus.COMPLETED); + var mockListener = performAction(viewport, actionBuilder.build()); + verify(mockListener).taskStatusChanged(any(), eq(status)); + } + + @Test(dataProvider = "jobStatuses") + public void jobStatusChanged_subJobStatusChangedCalledWithJobStatus(JobStatus status) + throws IOException + { + when(mockClient.getStatus(jobRef)) + .thenReturn(status) + .thenReturn(JobStatus.COMPLETED); + var mockListener = performAction(viewport, actionBuilder.build()); + verify(mockListener).subJobStatusChanged(any(), any(), eq(status)); + } +} \ No newline at end of file