From: Mateusz Warowny Date: Wed, 14 Jun 2023 14:25:28 +0000 (+0200) Subject: JAL-4199 Test URL settings and basic service discovery X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=eb6548a092a4175a931376d10fa819a09ed7a467;p=jalview.git JAL-4199 Test URL settings and basic service discovery --- diff --git a/src/jalview/ws2/client/slivka/SlivkaWSDiscoverer.java b/src/jalview/ws2/client/slivka/SlivkaWSDiscoverer.java index 2509a19..51e27ad 100644 --- a/src/jalview/ws2/client/slivka/SlivkaWSDiscoverer.java +++ b/src/jalview/ws2/client/slivka/SlivkaWSDiscoverer.java @@ -231,10 +231,9 @@ public class SlivkaWSDiscoverer extends AbstractWebServiceDiscoverer for (String classifier : service.getClassifiers()) { String[] path = classifier.split("\\s*::\\s*"); - if (path.length < 3 || !path[0].equalsIgnoreCase("operation") || - !path[1].equalsIgnoreCase("analysis")) + if (path.length < 3 || !path[0].equalsIgnoreCase("operation")) continue; - // classifier is operation :: analysis :: * + // classifier is operation :: * var tail = path[path.length - 1].toLowerCase(); switch (tail) { diff --git a/test/jalview/ws2/client/slivka/SlivkaWSDiscovererTest.java b/test/jalview/ws2/client/slivka/SlivkaWSDiscovererTest.java index 5519156..a656f14 100644 --- a/test/jalview/ws2/client/slivka/SlivkaWSDiscovererTest.java +++ b/test/jalview/ws2/client/slivka/SlivkaWSDiscovererTest.java @@ -1,33 +1,472 @@ package jalview.ws2.client.slivka; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import jalview.bin.Cache; +import jalview.bin.Console; +import jalview.ws2.actions.alignment.AlignmentAction; +import jalview.ws2.actions.annotation.AnnotationAction; +import jalview.ws2.client.api.WebServiceDiscovererI; +import uk.ac.dundee.compbio.slivkaclient.SlivkaClient; +import uk.ac.dundee.compbio.slivkaclient.SlivkaService; + public class SlivkaWSDiscovererTest { - @BeforeClass - public void setupClass() throws IOException + private static final String URLS_PROPERTY_NAME = "SLIVKAHOSTURLS"; + + SlivkaClient clientMock; + + Function factoryMock; + + @BeforeClass(alwaysRun = true) + public void setupProperties() + { + Cache.loadProperties("test/jalview/ws2/client/slivka/default.jvprops"); + Console.initLogger(); + } + + @BeforeMethod + public void setupDiscoverer() throws IOException + { + clientMock = mock(SlivkaClient.class); + } + + @Test + public void getStatusForUrl_servicesReturned_statusIsOK() throws Exception + { + when(clientMock.getServices()) + .thenReturn(List.of(mock(SlivkaService.class))); + var discoverer = new SlivkaWSDiscoverer( + url -> url.toString().equals("http://example.org") ? clientMock + : null); + assertThat(discoverer.getStatusForUrl(new URL("http://example.org")), + is(WebServiceDiscovererI.STATUS_OK)); + } + + @Test + public void getStatusForUrl_noServicesReturned_statusIsNoServices() + throws Exception + { + when(clientMock.getServices()).thenReturn(List.of()); + var discoverer = new SlivkaWSDiscoverer( + url -> url.toString().equals("http://example.org") ? clientMock + : null); + assertThat(discoverer.getStatusForUrl(new URL("http://example.org")), + is(WebServiceDiscovererI.STATUS_NO_SERVICES)); + } + + @Test + public void getStatusForUrl_exceptionThrown_statusIsInvalid() + throws Exception + { + when(clientMock.getServices()).thenThrow(new IOException()); + var discoverer = new SlivkaWSDiscoverer( + url -> url.toString().equals("http://example.org") ? clientMock + : null); + assertThat(discoverer.getStatusForUrl(new URL("http://example.org")), + is(WebServiceDiscovererI.STATUS_INVALID)); + } + + @Test + public void testGetUrls_noPropEntry_defaultUrlReturned() + throws MalformedURLException { var discoverer = SlivkaWSDiscoverer.getInstance(); - + assertThat(discoverer.getUrls(), + contains(new URL("https://www.compbio.dundee.ac.uk/slivka/"))); } - + + @DataProvider + public Object[][] urlPropertyValues() throws MalformedURLException + { + return new Object[][] { + { "http://example.org/", List.of(new URL("http://example.org/")) }, + { "https://example.org/slivka/", + List.of(new URL("https://example.org/slivka/")) }, + { "https://www.compbio.dundee.ac.uk/,http://www.example.org/", + List.of(new URL("https://www.compbio.dundee.ac.uk/"), + new URL("http://www.example.org/")) }, + { "http://example.org/,", List.of(new URL("http://example.org/")) }, + { ",http://example.org", List.of(new URL("http://example.org")) }, + { "", List.of() }, + { ",", List.of() }, + { "example.org", List.of() }, + { "example.org,http://example.org", + List.of(new URL("http://example.org")) } }; + } + + @Test(dataProvider = "urlPropertyValues") + public void testGetUrls_urlsProperlyParsed(String propValue, + List expected) + { + Cache.setProperty(URLS_PROPERTY_NAME, propValue); + var discoverer = SlivkaWSDiscoverer.getInstance(); + assertThat(discoverer.getUrls(), equalTo(expected)); + } + @Test - public void testServiceFetch() throws IOException + public void testSetUrls_emptyList_propertyReset() { + Cache.setProperty(URLS_PROPERTY_NAME, "http://www.example.org"); var discoverer = SlivkaWSDiscoverer.getInstance(); - var services = discoverer.fetchServices(discoverer.getDefaultUrl()); - for (var service : services) + discoverer.setUrls(List.of()); + assertThat(Cache.getProperty(URLS_PROPERTY_NAME), is(nullValue())); + } + + @Test + public void testSetUrls_null_propertyReset() + { + Cache.setProperty(URLS_PROPERTY_NAME, "http://www.example.org"); + var discoverer = SlivkaWSDiscoverer.getInstance(); + discoverer.setUrls(null); + assertThat(Cache.getProperty(URLS_PROPERTY_NAME), is(nullValue())); + } + + @DataProvider + public Object[][] urlsList() throws MalformedURLException + { + return new Object[][] { + { List.of(new URL("http://example.org")), "http://example.org" }, + { List.of(new URL("http://example.org/")), "http://example.org/" }, + { List.of(new URL("http://example.org/slivka/")), + "http://example.org/slivka/" }, + { List.of(new URL("https://www.compbio.dundee.ac.uk/slivka/"), + new URL("http://example.org")), + "https://www.compbio.dundee.ac.uk/slivka/,http://example.org" }, }; + } + + @Test(dataProvider = "urlsList") + public void testSetUrls_urlsPropertySet(List urls, String expected) + throws MalformedURLException + { + var discoverer = SlivkaWSDiscoverer.getInstance(); + discoverer.setUrls(urls); + assertThat(Cache.getProperty(URLS_PROPERTY_NAME), equalTo(expected)); + } + + @Test + public void testFetchServices_oneService_basicDataMatches() + throws IOException + { + var service = new SlivkaService( + URI.create("http://example.org/api/services/example"), + "example", "Example name", "Example service description", + "John Smith", "1.0", "MIT License", + List.of("operation::analysis::multiple sequence alignment"), + List.of(), List.of(), null); + when(clientMock.getServices()).thenReturn(List.of(service)); + when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/")); + var discoverer = new SlivkaWSDiscoverer(url -> clientMock); + var webServices = discoverer + .fetchServices(new URL("http://example.org/")); + assertThat(webServices, hasSize(1)); + var webService = webServices.get(0); + assertThat(webService.getUrl(), + equalTo(new URL("http://example.org/"))); + assertThat(webService.getClientName(), equalTo("slivka")); + assertThat(webService.getName(), equalTo("Example name")); + assertThat(webService.getDescription(), + equalTo("Example service description")); + } + + @DataProvider + public String[] validMultipleSequenceAlignmentClassifiers() + { + return new String[] { + "Operation :: Analysis :: Multiple sequence alignment", + "operation :: analysis :: multiple sequence alignment", + "Operation\t::\tAnalysis\t::\tMultiple sequence alignment", + "Operation::Analysis::Multiple sequence alignment", + "Operation :: Analysis :: Multiple Sequence Alignment", + "OPERATION :: ANALYSIS :: MULTIPLE SEQUENCE ALIGNMENT", + "Operation :: Analysis :: Sequence alignment :: Multiple sequence alignment", + "Operation :: Analysis :: Sequence analysis :: Sequence alignment :: Multiple sequence alignment", + "Operation :: Alignment :: Multiple sequence alignment", + "Operation :: Alignment :: Sequence alignment :: Multiple sequence alignment", + "Operation :: Comparison :: Multiple sequence alignment", + "Operation :: Comparison :: Sequence comparison :: Sequence alignment :: Multiple sequence alignment" }; + + } + + @Test(dataProvider = "validMultipleSequenceAlignmentClassifiers") + public void testFetchServices_multipleSequenceAlignmentClassifier_serviceTypeIsMSA( + String classifier) throws IOException + { + var service = new SlivkaService(URI.create("http://example.org/"), + "example", "name", "description", "author", "1.0", "MIT", + List.of(classifier), List.of(), List.of(), null); + when(clientMock.getServices()).thenReturn(List.of(service)); + when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/")); + var discoverer = new SlivkaWSDiscoverer(url -> clientMock); + var webServices = discoverer + .fetchServices(new URL("http://example.org/")); + assertThat(webServices, hasSize(1)); + assertThat(webServices.get(0).getCategory(), equalTo("Alignment")); + assertThat(webServices.get(0).getActionClass(), + typeCompatibleWith(AlignmentAction.class)); + } + + @DataProvider + public SlivkaService[] multipleSequenceAlignmentService() + { + return new SlivkaService[] { new SlivkaService( + URI.create("http://example.org/"), "example", "Examaple name", + "Example description", "John Smith", "1.0", "MIT", + List.of("Operation :: Analysis :: Multiple sequence alignment"), + List.of(), List.of(), null), + new SlivkaService( + URI.create("http://example.org/api/services/muscle"), + "muscle", "MUSCLE", + "MUltiple Sequence Comparison by Log- Expectation", + "Robert C. Edgar", "3.8.31", "Public domain", + List.of("Topic :: Computational biology :: Sequence analysis", + "Operation :: Analysis :: Sequence analysis :: Sequence alignment :: Multiple sequence alignment"), + List.of(), List.of(), null), + new SlivkaService( + URI.create("http://example.org/api/services/tcoffee"), + "tcoffee", "TCoffee", + "Tree-based Consistency Objective Function for Alignment Evaluation", + "Cedric Notredame", "13.41.0", "GNU GPL", + List.of("Topic :: Computational biology :: Sequence analysis", + "Operation :: Analysis :: Sequence analysis :: Sequence alignment :: Multiple sequence alignment"), + List.of(), List.of(), null) }; + } + + @Test(dataProvider = "multipleSequenceAlignmentService") + public void testFetchServices_multipleSequenceAlignmentService_actionTypeIsAlignment( + SlivkaService service) throws IOException + { + when(clientMock.getServices()).thenReturn(List.of(service)); + when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/")); + var discoverer = new SlivkaWSDiscoverer(url -> clientMock); + var webServices = discoverer + .fetchServices(new URL("http://example.org/")); + assertThat(webServices.get(0).getCategory(), equalTo("Alignment")); + assertThat(webServices.get(0).getActionClass(), + typeCompatibleWith(AlignmentAction.class)); + } + + @Test(dataProvider = "multipleSequenceAlignmentService") + public void testFetchServices_multipleSequenceAlignmentService_serviceIsNonInteractive( + SlivkaService service) throws IOException + { + when(clientMock.getServices()).thenReturn(List.of(service)); + when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/")); + var discoverer = new SlivkaWSDiscoverer(url -> clientMock); + var webServices = discoverer + .fetchServices(new URL("http://example.org/")); + assertThat(webServices.get(0).isInteractive(), is(false)); + } + + @DataProvider + public SlivkaService[] clustalFamilyService() + { + return new SlivkaService[] { new SlivkaService( + URI.create("http://example.org/api/services/clustalo"), + "clustalo", "ClustalO", + "Clustal Omega is the latest addition to the Clustal family.", + "Fabian Sievers, et al.", "1.2.4", "GNU GPL ver. 2", + List.of("Topic :: Computational biology :: Sequence analysis", + "Operation :: Analysis :: Sequence analysis :: Sequence alignment :: Multiple sequence alignment"), + List.of(), List.of(), null), + new SlivkaService( + URI.create("http://example.org/api/services/clustalw"), + "clustalw", "ClustalW", + "ClustalW is a general purpose multiple alignment program.", + "Larkin MA, et al.", "2.1", "GNU GPL ver. 3", + List.of("Topic :: Computation biology :: Sequence analysis", + "Operation :: Analysis :: Multiple sequence alignment"), + List.of(), List.of(), null), + new SlivkaService( + URI.create("http://example.org/api/services/clustalw2"), + "clustalw2", "ClustalW2", + "ClustalW is a general purpose multiple alignment program.", + "Larkin MA, et al.", "2.1", "GNU GPL ver. 3", + List.of("Topic :: Computation biology :: Sequence analysis", + "Operation :: Analysis :: Multiple sequence alignment"), + List.of(), List.of(), null), }; + } + + @Test(dataProvider = "clustalFamilyService") + public void testFetchService_clustalFamilyService_containsTwoActions( + SlivkaService service) throws IOException + { + when(clientMock.getServices()).thenReturn(List.of(service)); + when(clientMock.getUrl()).thenReturn(URI.create("http://example.org")); + var discoverer = new SlivkaWSDiscoverer(url -> clientMock); + var webServices = discoverer + .fetchServices(new URL("http://example.org")); + var actions = webServices.get(0).getActions(); + assertThat(actions, hasSize(2)); + assertThat(actions.get(0), allOf(hasProperty("name", is("Alignment")), + hasProperty("subcategory", is("Align")))); + assertThat(actions.get(1), + allOf(hasProperty("name", is("Re-alignment")), + hasProperty("subcategory", is("Realign")))); + } + + @DataProvider + public String[] validRNASecondaryStructurePredictionClassifiers() + { + return new String[] { + "Operation :: Analysis :: RNA secondary structure prediction", + "operation :: analysis :: rna secondary structure prediction", + "OPERATION :: ANALYSIS :: RNA SECONDARY STRUCTURE PREDICTION", + "Operation\t::\tAnalysis\t::\tRNA secondary structure prediction", + "Operation::Analysis::RNA secondary structure prediction", + "Operation :: Analysis :: Structure analysis :: RNA secondary structure prediction", + "Operation :: Analysis :: Structure analysis :: Nucleic acid structure analysis :: RNA secondary structure analysis :: RNA secondary structure prediction", + "Operation :: Analysis :: Structure analysis :: Nucleic acid structure analysis :: Nucleic acid structure prediction :: RNA secondary structure prediction", + "Operation :: Analysis :: Sequence analysis :: Nucleic acid sequence analysis :: Nucleic acid feature detection :: RNA secondary structure prediction", + "Operation :: Prediction and recognition :: RNA secondary structure prediction", + "Operation :: Prediction and recognition :: Nucleic acid feature detection :: RNA secondary structure prediction", + "Operation :: Prediction and recignition :: Nucleic acid structure prediction :: RNA secondary structure prediction", }; + } + + @DataProvider + public Iterator RNASecondaryStructurePredictionService() + { + var services = new ArrayList<>(); + for (var classifier : validRNASecondaryStructurePredictionClassifiers()) { - System.out.format("Service(%s>%s @%s)%n", service.getCategory(), - service.getName(), service.getUrl()); - var datastore = service.getParamDatastore(); - for (var param : datastore.getServiceParameters()) - { - System.out.format(" %s :%s%n", param.getName(), param.getClass().getSimpleName()); - } + services.add(new SlivkaService(URI.create("http://example.org/"), + "example", "name", "description", "author", "1.0", "MIT", + List.of(classifier), List.of(), List.of(), null)); } + return services.iterator(); + } + + @Test(dataProvider = "RNASecondaryStructurePredictionService") + public void testFetchServices_RNASecStrPredClassifier_serviceTypeIsRNASecStrPred( + SlivkaService service) throws IOException + { + when(clientMock.getServices()).thenReturn(List.of(service)); + when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/")); + var discoverer = new SlivkaWSDiscoverer(url -> clientMock); + var webServices = discoverer + .fetchServices(new URL("http://example.org/")); + assertThat(webServices, hasSize(1)); + assertThat(webServices.get(0).getCategory(), + equalTo("Secondary Structure Prediction")); + assertThat(webServices.get(0).getActionClass(), + typeCompatibleWith(AnnotationAction.class)); + } + + @DataProvider + public String[] validConservationAnalysisClassifiers() + { + return new String[] { + "Operation :: Analysis :: Sequence alignment analysis (conservation)", + "Operation::Analysis::Sequence alignment analysis (conservation)", + "Operation\t::\tAnalysis\t::\tSequence alignment analysis (conservation)", + "Operation :: Analysis :: Sequence analysis :: Sequence alignment analysis (conservation)", + "Operation :: Analysis :: Sequence analysis :: Sequence alignment analysis :: Sequence alignment analysis (conservation)", }; + } + + @DataProvider + public Iterator ConservationAnalysisService() + { + var services = new ArrayList<>(); + for (var classifier : validConservationAnalysisClassifiers()) + { + services.add(new SlivkaService(URI.create("http://example.org/"), + "example", "name", "description", "author", "1.0", "MIT", + List.of(classifier), List.of(), List.of(), null)); + } + return services.iterator(); + } + + @Test(dataProvider = "validConservationAnalysisClassifiers") + public void testFetchServices_conservationAnalysisClassifier_serviceTypeIsConservation( + String classifier) throws IOException + { + var service = new SlivkaService(URI.create("http://example.org/"), + "example", "name", "description", "author", "1.0", "MIT", + List.of(classifier), List.of(), List.of(), null); + when(clientMock.getServices()).thenReturn(List.of(service)); + when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/")); + var discoverer = new SlivkaWSDiscoverer(url -> clientMock); + var webServices = discoverer + .fetchServices(new URL("http://example.org/")); + assertThat(webServices, hasSize(1)); + assertThat(webServices.get(0).getCategory(), equalTo("Conservation")); + assertThat(webServices.get(0).getActionClass(), + typeCompatibleWith(AnnotationAction.class)); + } + + @DataProvider + public Object[] validProteinSequenceAnalysisClassifiers() + { + return new Object[] { + "Operation :: Analysis :: Sequence analysis :: Protein sequence analysis", }; + } + + @Test(dataProvider = "validProteinSequenceAnalysisClassifiers") + public void testFetchServices_proteinSequenceAnalysisClassifier_serviceTypeIsProtSeqAnalysis( + String classifier) throws IOException + { + var service = new SlivkaService(URI.create("http://example.org/"), + "example", "name", "description", "author", "1.0", "MIT", + List.of(classifier), List.of(), List.of(), null); + when(clientMock.getServices()).thenReturn(List.of(service)); + when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/")); + var discoverer = new SlivkaWSDiscoverer(url -> clientMock); + var webServices = discoverer + .fetchServices(new URL("http://example.org/")); + assertThat(webServices, hasSize(1)); + assertThat(webServices.get(0).getCategory(), + equalTo("Protein Disorder")); + assertThat(webServices.get(0).getActionClass(), + typeCompatibleWith(AnnotationAction.class)); + } + + @DataProvider + public Object[] validProteinSecondaryStructurePredictionClassifiers() + { + return new Object[] { + "Operation ;: Analysis :: Protein secondary structure prediction", + "Operation :: Analysis :: Structure analysis :: Protein structure analysis :: Protein secondary structure analysis :: Protein secondary structure prediction", + "Operation :: Analysis :: Sequence analysis :: Protein sequence analysis :: Protein feature detection :: Protein secondary structure prediction", + "Operation :: Analysis :: Sequence analysis :: Protein sequence analysis :: Protein secondary structure prediction", + "Operation :: Prediction and recognition :: Protein secondary structure prediction", + "Operation :: Prediction and recognition :: Protein feature detection :: Protein secondary structure prediction", }; + } + + @Test( + enabled = false, // sec. str. pred. not implemented for slivka + dataProvider = "validProteinSecondaryStructurePredictionClassifiers") + public void testFetchServices_proteinSecStrPredClassifier_serviceTypeIsProtSecStrPred( + String classifier) throws IOException + { + var service = new SlivkaService(URI.create("http://example.org/"), + "example", "name", "description", "author", "1.0", "MIT", + List.of(classifier), List.of(), List.of(), null); + when(clientMock.getServices()).thenReturn(List.of(service)); + when(clientMock.getUrl()).thenReturn(URI.create("http://example.org/")); + var discoverer = new SlivkaWSDiscoverer(url -> clientMock); + var webServices = discoverer + .fetchServices(new URL("http://example.org/")); + assertThat(webServices, hasSize(1)); + assertThat(webServices.get(0).getCategory(), + equalTo("Protein Disorder")); + assertThat(webServices.get(0).getActionClass(), + typeCompatibleWith(AnnotationAction.class)); } } diff --git a/test/jalview/ws2/client/slivka/default.jvprops b/test/jalview/ws2/client/slivka/default.jvprops new file mode 100644 index 0000000..190ca63 --- /dev/null +++ b/test/jalview/ws2/client/slivka/default.jvprops @@ -0,0 +1,2 @@ +#---JalviewX Properties File--- +#Wed Jun 07 18:01:12 CET 2023 diff --git a/utils/testnglibs/hamcrest-2.2-sources.jar b/utils/testnglibs/hamcrest-2.2-sources.jar new file mode 100644 index 0000000..6124211 Binary files /dev/null and b/utils/testnglibs/hamcrest-2.2-sources.jar differ diff --git a/utils/testnglibs/hamcrest-2.2.jar b/utils/testnglibs/hamcrest-2.2.jar new file mode 100644 index 0000000..7106578 Binary files /dev/null and b/utils/testnglibs/hamcrest-2.2.jar differ