Commit 7e59bea6 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

Merge branch 'search-by-synonyms-in-chembl' into 'devel_13.1.x'

Search by synonyms in chembl

See merge request !885
parents 90fad475 d4db2a49
Pipeline #12952 canceled with stage
in 6 minutes and 4 seconds
minerva (13.1.4) stable; urgency=medium
* Bug fix:search by drugs didn't check synonyms in chembl database
-- Piotr Gawron <piotr.gawron@uni.lu> Wed, 21 Aug 2019 17:00:00 +0200
minerva (13.1.3) stable; urgency=medium minerva (13.1.3) stable; urgency=medium
* Bug fix: refreshing list of projects or list of users doesn't change active * Bug fix: refreshing list of projects or list of users doesn't change active
page (#870) page (#870)
......
...@@ -64,6 +64,11 @@ public class ChEMBLParser extends DrugAnnotation implements IExternalService { ...@@ -64,6 +64,11 @@ public class ChEMBLParser extends DrugAnnotation implements IExternalService {
*/ */
private static final String DRUG_NAME_API_URL = "https://www.ebi.ac.uk/chembl/api/data/molecule?pref_name__exact="; private static final String DRUG_NAME_API_URL = "https://www.ebi.ac.uk/chembl/api/data/molecule?pref_name__exact=";
/**
* Url that access data of drug identified by name.
*/
private static final String DRUG_SYNONYM_API_URL = "https://www.ebi.ac.uk/chembl/api/data/molecule?molecule_synonyms__molecule_synonym__iexact=";
/** /**
* Url that access target by identifier. * Url that access target by identifier.
*/ */
...@@ -366,29 +371,22 @@ public class ChEMBLParser extends DrugAnnotation implements IExternalService { ...@@ -366,29 +371,22 @@ public class ChEMBLParser extends DrugAnnotation implements IExternalService {
String page = getWebPageContent(accessUrl); String page = getWebPageContent(accessUrl);
Document document = XmlParser.getXmlDocumentFromString(page); Document document = XmlParser.getXmlDocumentFromString(page);
Node response = XmlParser.getNode("response", document.getChildNodes()); List<Drug> drugs = extractDrugsFromDocument(document);
Node molecules = XmlParser.getNode("molecules", response.getChildNodes()); if (drugs.size()==0) {
accessUrl = DRUG_SYNONYM_API_URL + name;
NodeList list = molecules.getChildNodes(); page = getWebPageContent(accessUrl);
document = XmlParser.getXmlDocumentFromString(page);
for (int i = 0; i < list.getLength(); i++) { drugs = extractDrugsFromDocument(document);
Node node = list.item(i); }
if (node.getNodeType() == Node.ELEMENT_NODE) { if (drugs.size()>1) {
if (node.getNodeName().equalsIgnoreCase("molecule")) { logger.warn("More drugs than one found for query: " + query);
if (drug == null) {
drug = parseDrug(node);
} else {
logger.warn("More drugs than one found for query: " + query);
}
}
}
} }
if (drug == null) { if (drugs.size() == 0) {
return null; return null;
} }
setCacheValue(query, getDrugSerializer().objectToString(drug)); setCacheValue(query, getDrugSerializer().objectToString(drugs.get(0)));
return drug; return drugs.get(0);
} catch (IOException e) { } catch (IOException e) {
throw new DrugSearchException("Problem with accessing CHEMBL database", e); throw new DrugSearchException("Problem with accessing CHEMBL database", e);
} catch (InvalidXmlSchemaException e) { } catch (InvalidXmlSchemaException e) {
...@@ -396,6 +394,24 @@ public class ChEMBLParser extends DrugAnnotation implements IExternalService { ...@@ -396,6 +394,24 @@ public class ChEMBLParser extends DrugAnnotation implements IExternalService {
} }
} }
private List<Drug> extractDrugsFromDocument(Document document) throws DrugSearchException {
List<Drug> result = new ArrayList<>();
Node response = XmlParser.getNode("response", document.getChildNodes());
Node molecules = XmlParser.getNode("molecules", response.getChildNodes());
NodeList list = molecules.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
if (node.getNodeName().equalsIgnoreCase("molecule")) {
result.add(parseDrug(node));
}
}
}
return result;
}
/** /**
* Parse xml node repesenting drug and returns drug with information obtained * Parse xml node repesenting drug and returns drug with information obtained
* from there and other places in chembl API. * from there and other places in chembl API.
......
package lcsb.mapviewer.annotation.services; package lcsb.mapviewer.annotation.services;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.*;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.junit.Test; import org.junit.Test;
...@@ -23,22 +14,11 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -23,22 +14,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import lcsb.mapviewer.annotation.AnnotationTestFunctions; import lcsb.mapviewer.annotation.AnnotationTestFunctions;
import lcsb.mapviewer.annotation.cache.GeneralCacheInterface; import lcsb.mapviewer.annotation.cache.*;
import lcsb.mapviewer.annotation.cache.PermanentDatabaseLevelCacheInterface; import lcsb.mapviewer.annotation.data.*;
import lcsb.mapviewer.annotation.cache.SourceNotAvailable; import lcsb.mapviewer.annotation.services.annotators.*;
import lcsb.mapviewer.annotation.cache.WebPageDownloader;
import lcsb.mapviewer.annotation.cache.XmlSerializer;
import lcsb.mapviewer.annotation.data.Drug;
import lcsb.mapviewer.annotation.data.Target;
import lcsb.mapviewer.annotation.data.TargetType;
import lcsb.mapviewer.annotation.services.annotators.AnnotatorException;
import lcsb.mapviewer.annotation.services.annotators.HgncAnnotator;
import lcsb.mapviewer.annotation.services.annotators.UniprotAnnotator;
import lcsb.mapviewer.annotation.services.annotators.UniprotSearchException;
import lcsb.mapviewer.common.exception.InvalidArgumentException; import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.model.map.MiriamData; import lcsb.mapviewer.model.map.*;
import lcsb.mapviewer.model.map.MiriamRelationType;
import lcsb.mapviewer.model.map.MiriamType;
public class ChEMBLParserTest extends AnnotationTestFunctions { public class ChEMBLParserTest extends AnnotationTestFunctions {
Logger logger = Logger.getLogger(ChEMBLParserTest.class); Logger logger = Logger.getLogger(ChEMBLParserTest.class);
...@@ -349,7 +329,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions { ...@@ -349,7 +329,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions {
try { try {
chemblParser.setCache(null); chemblParser.setCache(null);
WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class); WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class);
when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class))).thenThrow(new IOException()); when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class)))
.thenThrow(new IOException());
chemblParser.setWebPageDownloader(mockDownloader); chemblParser.setWebPageDownloader(mockDownloader);
chemblParser.getTargetFromId(new MiriamData(MiriamType.CHEMBL_TARGET, "water")); chemblParser.getTargetFromId(new MiriamData(MiriamType.CHEMBL_TARGET, "water"));
fail("Exception expected"); fail("Exception expected");
...@@ -411,7 +392,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions { ...@@ -411,7 +392,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions {
try { try {
chemblParser.setCache(null); chemblParser.setCache(null);
WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class); WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class);
when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class))).thenReturn("<target><unk/></target>"); when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class)))
.thenReturn("<target><unk/></target>");
chemblParser.setWebPageDownloader(mockDownloader); chemblParser.setWebPageDownloader(mockDownloader);
chemblParser.getTargetFromId(new MiriamData(MiriamType.CHEMBL_TARGET, "water")); chemblParser.getTargetFromId(new MiriamData(MiriamType.CHEMBL_TARGET, "water"));
assertEquals(1, getWarnings().size()); assertEquals(1, getWarnings().size());
...@@ -842,7 +824,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions { ...@@ -842,7 +824,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions {
// simulate problem with downloading // simulate problem with downloading
WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class); WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class);
when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class))).thenThrow(new IOException()); when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class)))
.thenThrow(new IOException());
chemblParser.setWebPageDownloader(mockDownloader); chemblParser.setWebPageDownloader(mockDownloader);
chemblParser.refreshCacheQuery("http://google.pl/"); chemblParser.refreshCacheQuery("http://google.pl/");
fail("Exception expected"); fail("Exception expected");
...@@ -866,7 +849,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions { ...@@ -866,7 +849,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions {
// simulate problem with downloading // simulate problem with downloading
WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class); WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class);
when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class))).thenThrow(new IOException()); when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class)))
.thenThrow(new IOException());
chemblParser.setWebPageDownloader(mockDownloader); chemblParser.setWebPageDownloader(mockDownloader);
chemblParser.getDrugsByChemblTarget(new MiriamData()); chemblParser.getDrugsByChemblTarget(new MiriamData());
...@@ -920,7 +904,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions { ...@@ -920,7 +904,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions {
// simulate problem with downloading // simulate problem with downloading
WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class); WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class);
when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class))).thenThrow(new IOException()); when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class)))
.thenThrow(new IOException());
chemblParser.setWebPageDownloader(mockDownloader); chemblParser.setWebPageDownloader(mockDownloader);
chemblParser.refreshCacheQuery(query); chemblParser.refreshCacheQuery(query);
fail("Exception expected"); fail("Exception expected");
...@@ -949,7 +934,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions { ...@@ -949,7 +934,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions {
WebPageDownloader downloader = chemblParser.getWebPageDownloader(); WebPageDownloader downloader = chemblParser.getWebPageDownloader();
try { try {
WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class); WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class);
when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class))).thenThrow(new IOException()); when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class)))
.thenThrow(new IOException());
chemblParser.setWebPageDownloader(mockDownloader); chemblParser.setWebPageDownloader(mockDownloader);
assertEquals(ExternalServiceStatusType.DOWN, chemblParser.getServiceStatus().getStatus()); assertEquals(ExternalServiceStatusType.DOWN, chemblParser.getServiceStatus().getStatus());
} catch (Exception e) { } catch (Exception e) {
...@@ -984,9 +970,10 @@ public class ChEMBLParserTest extends AnnotationTestFunctions { ...@@ -984,9 +970,10 @@ public class ChEMBLParserTest extends AnnotationTestFunctions {
WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class); WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class);
// valid xml but with empty data // valid xml but with empty data
when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class))).thenReturn("<response><molecules>" when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class)))
+ "<molecule><pref_name/><max_phase/><molecule_chembl_id/><molecule_synonyms/></molecule>" .thenReturn("<response><molecules>"
+ "</molecules><mechanisms/><molecule_forms/></response>"); + "<molecule><pref_name/><max_phase/><molecule_chembl_id/><molecule_synonyms/></molecule>"
+ "</molecules><mechanisms/><molecule_forms/></response>");
chemblParser.setWebPageDownloader(mockDownloader); chemblParser.setWebPageDownloader(mockDownloader);
assertEquals(ExternalServiceStatusType.CHANGED, chemblParser.getServiceStatus().getStatus()); assertEquals(ExternalServiceStatusType.CHANGED, chemblParser.getServiceStatus().getStatus());
} catch (Exception e) { } catch (Exception e) {
...@@ -1043,7 +1030,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions { ...@@ -1043,7 +1030,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions {
try { try {
chemblParser.setCache(null); chemblParser.setCache(null);
WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class); WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class);
when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class))).thenThrow(new IOException()); when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class)))
.thenThrow(new IOException());
chemblParser.setWebPageDownloader(mockDownloader); chemblParser.setWebPageDownloader(mockDownloader);
chemblParser.getTargetsByDrugId(new MiriamData(MiriamType.CHEMBL_COMPOUND, "123")); chemblParser.getTargetsByDrugId(new MiriamData(MiriamType.CHEMBL_COMPOUND, "123"));
fail("Exception expected"); fail("Exception expected");
...@@ -1063,7 +1051,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions { ...@@ -1063,7 +1051,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions {
try { try {
chemblParser.setCache(null); chemblParser.setCache(null);
WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class); WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class);
when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class))).thenThrow(new IOException()); when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class)))
.thenThrow(new IOException());
chemblParser.setWebPageDownloader(mockDownloader); chemblParser.setWebPageDownloader(mockDownloader);
chemblParser.getDrugListByTarget(new MiriamData(MiriamType.UNIPROT, "O60391")); chemblParser.getDrugListByTarget(new MiriamData(MiriamType.UNIPROT, "O60391"));
fail("Exception expected"); fail("Exception expected");
...@@ -1165,7 +1154,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions { ...@@ -1165,7 +1154,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions {
try { try {
chemblParser.setCache(null); chemblParser.setCache(null);
WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class); WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class);
when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class))).thenThrow(new IOException()); when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class)))
.thenThrow(new IOException());
chemblParser.setWebPageDownloader(mockDownloader); chemblParser.setWebPageDownloader(mockDownloader);
chemblParser.getDrugById(new MiriamData(MiriamType.CHEMBL_COMPOUND, "")); chemblParser.getDrugById(new MiriamData(MiriamType.CHEMBL_COMPOUND, ""));
fail("Exception expected"); fail("Exception expected");
...@@ -1199,7 +1189,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions { ...@@ -1199,7 +1189,8 @@ public class ChEMBLParserTest extends AnnotationTestFunctions {
try { try {
chemblParser.setCache(null); chemblParser.setCache(null);
WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class); WebPageDownloader mockDownloader = Mockito.mock(WebPageDownloader.class);
when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class))).thenThrow(new IOException()); when(mockDownloader.getFromNetwork(anyString(), anyString(), nullable(String.class)))
.thenThrow(new IOException());
chemblParser.setWebPageDownloader(mockDownloader); chemblParser.setWebPageDownloader(mockDownloader);
chemblParser.getTargetsForChildElements(new MiriamData()); chemblParser.getTargetsForChildElements(new MiriamData());
fail("Exception expected"); fail("Exception expected");
...@@ -1253,4 +1244,16 @@ public class ChEMBLParserTest extends AnnotationTestFunctions { ...@@ -1253,4 +1244,16 @@ public class ChEMBLParserTest extends AnnotationTestFunctions {
} }
} }
@Test
public void testFindDrugBryBrandName() throws Exception {
try {
Drug drug = chemblParser.findDrug("picato");
assertNotNull(drug);
assertEquals("CHEMBL1863513", drug.getSources().get(0).getResource());
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
} }
delete from cache_query_table where type in (select id from cache_type_table where class_name ='lcsb.mapviewer.annotation.services.ChEMBLParser');
package lcsb.mapviewer.services.search.drug; package lcsb.mapviewer.services.search.drug;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -14,21 +9,14 @@ import org.springframework.transaction.annotation.Transactional; ...@@ -14,21 +9,14 @@ import org.springframework.transaction.annotation.Transactional;
import lcsb.mapviewer.annotation.data.Drug; import lcsb.mapviewer.annotation.data.Drug;
import lcsb.mapviewer.annotation.data.Target; import lcsb.mapviewer.annotation.data.Target;
import lcsb.mapviewer.annotation.services.ChEMBLParser; import lcsb.mapviewer.annotation.services.*;
import lcsb.mapviewer.annotation.services.DrugSearchException;
import lcsb.mapviewer.annotation.services.DrugbankHTMLParser;
import lcsb.mapviewer.annotation.services.annotators.AnnotatorException; import lcsb.mapviewer.annotation.services.annotators.AnnotatorException;
import lcsb.mapviewer.annotation.services.annotators.HgncAnnotator; import lcsb.mapviewer.annotation.services.annotators.HgncAnnotator;
import lcsb.mapviewer.common.IProgressUpdater; import lcsb.mapviewer.common.IProgressUpdater;
import lcsb.mapviewer.model.Project; import lcsb.mapviewer.model.Project;
import lcsb.mapviewer.model.map.BioEntity; import lcsb.mapviewer.model.map.*;
import lcsb.mapviewer.model.map.MiriamData;
import lcsb.mapviewer.model.map.MiriamType;
import lcsb.mapviewer.model.map.model.Model; import lcsb.mapviewer.model.map.model.Model;
import lcsb.mapviewer.model.map.species.Element; import lcsb.mapviewer.model.map.species.*;
import lcsb.mapviewer.model.map.species.Gene;
import lcsb.mapviewer.model.map.species.Protein;
import lcsb.mapviewer.model.map.species.Rna;
import lcsb.mapviewer.model.map.statistics.SearchType; import lcsb.mapviewer.model.map.statistics.SearchType;
import lcsb.mapviewer.services.interfaces.ISearchHistoryService; import lcsb.mapviewer.services.interfaces.ISearchHistoryService;
import lcsb.mapviewer.services.search.DbSearchCriteria; import lcsb.mapviewer.services.search.DbSearchCriteria;
...@@ -185,9 +173,15 @@ public class DrugService extends DbSearchService implements IDrugService { ...@@ -185,9 +173,15 @@ public class DrugService extends DbSearchService implements IDrugService {
if (name.trim().equals("")) { if (name.trim().equals("")) {
return null; return null;
} }
DrugAnnotation secondParser = chEMBLParser;
Drug drug = null; Drug drug = null;
try { try {
drug = drugBankParser.findDrug(name); drug = drugBankParser.findDrug(name);
if (drug==null) {
drug = chEMBLParser.findDrug(name);
secondParser = drugBankParser;
}
if (drug != null) { if (drug != null) {
removeUnknownOrganisms(drug, searchCriteria.getOrganisms()); removeUnknownOrganisms(drug, searchCriteria.getOrganisms());
} }
...@@ -201,13 +195,13 @@ public class DrugService extends DbSearchService implements IDrugService { ...@@ -201,13 +195,13 @@ public class DrugService extends DbSearchService implements IDrugService {
// * search for name // * search for name
searchNames.add(name); searchNames.add(name);
if (drug != null && drug.getName() != null) { if (drug != null && drug.getName() != null) {
// * search for name of a drug in drugbanke // * search for name of a drug in drugbank
searchNames.add(drug.getName()); searchNames.add(drug.getName());
// * search for all synonyms found in drugbank // * search for all synonyms found in drugbank
// searchNames.addAll(drug.getSynonyms()); // searchNames.addAll(drug.getSynonyms());
} }
for (String string : searchNames) { for (String string : searchNames) {
Drug drug2 = chEMBLParser.findDrug(string); Drug drug2 = secondParser.findDrug(string);
if (drug2 != null) { if (drug2 != null) {
removeUnknownOrganisms(drug2, searchCriteria.getOrganisms()); removeUnknownOrganisms(drug2, searchCriteria.getOrganisms());
// don't add drugs that were already found // don't add drugs that were already found
......
...@@ -57,6 +57,17 @@ public class DrugServiceTest extends ServiceTestFunctions { ...@@ -57,6 +57,17 @@ public class DrugServiceTest extends ServiceTestFunctions {
} }
} }
@Test
public void testGetIstodax() throws Exception {
try {
Drug drug = drugService.getByName("istodax", new DbSearchCriteria());
assertEquals(2, drug.getSources().size());
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test @Test
public void testGetTargets() throws Exception { public void testGetTargets() throws Exception {
try { try {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment