Merge branch 'develop' into update_212_Dec_merge_with_21125_chamges
[jalview.git] / test / jalview / analysis / CrossRefTest.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.analysis;
22
23 import static org.testng.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertFalse;
25 import static org.testng.AssertJUnit.assertNotNull;
26 import static org.testng.AssertJUnit.assertNotSame;
27 import static org.testng.AssertJUnit.assertNull;
28 import static org.testng.AssertJUnit.assertSame;
29 import static org.testng.AssertJUnit.assertTrue;
30
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
34
35 import org.testng.annotations.AfterClass;
36 import org.testng.annotations.BeforeClass;
37 import org.testng.annotations.Test;
38
39 import jalview.datamodel.AlignedCodonFrame;
40 import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
41 import jalview.datamodel.Alignment;
42 import jalview.datamodel.AlignmentI;
43 import jalview.datamodel.DBRefEntry;
44 import jalview.datamodel.Mapping;
45 import jalview.datamodel.Sequence;
46 import jalview.datamodel.SequenceFeature;
47 import jalview.datamodel.SequenceI;
48 import jalview.gui.JvOptionPane;
49 import jalview.util.DBRefUtils;
50 import jalview.util.MapList;
51 import jalview.ws.SequenceFetcher;
52
53 public class CrossRefTest
54 {
55
56   @BeforeClass(alwaysRun = true)
57   public void setUpJvOptionPane()
58   {
59     JvOptionPane.setInteractiveMode(false);
60     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
61   }
62
63   @Test(groups = { "Functional" })
64   public void testFindXDbRefs()
65   {
66     DBRefEntry ref1 = new DBRefEntry("UNIPROT", "1", "A123");
67     DBRefEntry ref2 = new DBRefEntry("UNIPROTKB/TREMBL", "1", "A123");
68     DBRefEntry ref3 = new DBRefEntry("pdb", "1", "A123");
69     DBRefEntry ref4 = new DBRefEntry("EMBLCDSPROTEIN", "1", "A123");
70     DBRefEntry ref5 = new DBRefEntry("embl", "1", "A123");
71     DBRefEntry ref6 = new DBRefEntry("emblCDS", "1", "A123");
72     DBRefEntry ref7 = new DBRefEntry("GeneDB", "1", "A123");
73     DBRefEntry ref8 = new DBRefEntry("PFAM", "1", "A123");
74     // ENSEMBL is a source of either dna or protein sequence data
75     DBRefEntry ref9 = new DBRefEntry("ENSEMBL", "1", "A123");
76     List<DBRefEntry> refs = Arrays
77             .asList(new DBRefEntry[]
78             { ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9 });
79
80     /*
81      * Just the DNA refs:
82      */
83     List<DBRefEntry> found = DBRefUtils.selectDbRefs(true, refs);
84     assertEquals(4, found.size());
85     assertSame(ref5, found.get(0));
86     assertSame(ref6, found.get(1));
87     assertSame(ref7, found.get(2));
88     assertSame(ref9, found.get(3));
89
90     /*
91      * Just the protein refs:
92      */
93     found = DBRefUtils.selectDbRefs(false, refs);
94     assertEquals(4, found.size());
95     assertSame(ref1, found.get(0));
96     assertSame(ref2, found.get(1));
97     assertSame(ref4, found.get(2));
98     assertSame(ref9, found.get(3));
99   }
100
101   /**
102    * Test the method that finds a sequence's "product" xref source databases,
103    * which may be direct (dbrefs on the sequence), or indirect (dbrefs on
104    * sequences which share a dbref with the sequence
105    */
106   @Test(groups = { "Functional" }, enabled = true)
107   public void testFindXrefSourcesForSequence_proteinToDna()
108   {
109     SequenceI seq = new Sequence("Seq1", "MGKYQARLSS");
110     List<String> sources = new ArrayList<>();
111     AlignmentI al = new Alignment(new SequenceI[] {});
112
113     /*
114      * first with no dbrefs to search
115      */
116     sources = new CrossRef(new SequenceI[] { seq }, al)
117             .findXrefSourcesForSequences(false);
118     assertTrue(sources.isEmpty());
119
120     /*
121      * add some dbrefs to sequence
122      */
123     // protein db is not a candidate for findXrefSources
124     seq.addDBRef(new DBRefEntry("UNIPROT", "0", "A1234"));
125     // dna coding databatases are
126     seq.addDBRef(new DBRefEntry("EMBL", "0", "E2345"));
127     // a second EMBL xref should not result in a duplicate
128     seq.addDBRef(new DBRefEntry("EMBL", "0", "E2346"));
129     seq.addDBRef(new DBRefEntry("EMBLCDS", "0", "E2347"));
130     seq.addDBRef(new DBRefEntry("GENEDB", "0", "E2348"));
131     seq.addDBRef(new DBRefEntry("ENSEMBL", "0", "E2349"));
132     seq.addDBRef(new DBRefEntry("ENSEMBLGENOMES", "0", "E2350"));
133     sources = new CrossRef(new SequenceI[] { seq }, al)
134             .findXrefSourcesForSequences(false);
135     // method is patched to remove EMBL from the sources to match
136     assertEquals(4, sources.size());
137     assertEquals("[EMBLCDS, GENEDB, ENSEMBL, ENSEMBLGENOMES]",
138             sources.toString());
139
140     /*
141      * add a sequence to the alignment which has a dbref to UNIPROT|A1234
142      * and others to dna coding databases
143      */
144     sources.clear();
145     seq.setDBRefs(null);
146     seq.addDBRef(new DBRefEntry("UNIPROT", "0", "A1234"));
147     seq.addDBRef(new DBRefEntry("EMBLCDS", "0", "E2347"));
148     SequenceI seq2 = new Sequence("Seq2", "MGKYQARLSS");
149     seq2.addDBRef(new DBRefEntry("UNIPROT", "0", "A1234"));
150     seq2.addDBRef(new DBRefEntry("EMBL", "0", "E2345"));
151     seq2.addDBRef(new DBRefEntry("GENEDB", "0", "E2348"));
152     // TODO include ENSEMBLGENOMES in DBRefSource.DNACODINGDBS ?
153     al.addSequence(seq2);
154     sources = new CrossRef(new SequenceI[] { seq, seq2 }, al)
155             .findXrefSourcesForSequences(false);
156     // method removed EMBL from sources to match
157     assertEquals(2, sources.size());
158     assertEquals("[EMBLCDS, GENEDB]", sources.toString());
159   }
160
161   /**
162    * Test for finding 'product' sequences for the case where only an indirect
163    * xref is found - not on the nucleotide sequence but on a peptide sequence in
164    * the alignment which which it shares a nucleotide dbref
165    */
166   @Test(groups = { "Functional" }, enabled = true)
167   public void testFindXrefSequences_indirectDbrefToProtein()
168   {
169     /*
170      * Alignment setup:
171      *   - nucleotide dbref  EMBL|AF039662
172      *   - peptide    dbrefs EMBL|AF039662, UNIPROT|Q9ZTS2
173      */
174     SequenceI emblSeq = new Sequence("AF039662", "GGGGCAGCACAAGAAC");
175     emblSeq.addDBRef(new DBRefEntry("EMBL", "0", "AF039662"));
176     SequenceI uniprotSeq = new Sequence("Q9ZTS2", "MASVSATMISTS");
177     uniprotSeq.addDBRef(new DBRefEntry("EMBL", "0", "AF039662"));
178     uniprotSeq.addDBRef(new DBRefEntry("UNIPROT", "0", "Q9ZTS2"));
179
180     /*
181      * Find UNIPROT xrefs for nucleotide 
182      * - it has no UNIPROT dbref of its own
183      * - but peptide with matching nucleotide dbref does, so is returned
184      */
185     AlignmentI al = new Alignment(new SequenceI[] { emblSeq, uniprotSeq });
186     Alignment xrefs = new CrossRef(new SequenceI[] { emblSeq }, al)
187             .findXrefSequences("UNIPROT", true);
188     assertEquals(1, xrefs.getHeight());
189     assertSame(uniprotSeq, xrefs.getSequenceAt(0));
190   }
191
192   /**
193    * Test for finding 'product' sequences for the case where only an indirect
194    * xref is found - not on the peptide sequence but on a nucleotide sequence in
195    * the alignment which which it shares a protein dbref
196    */
197   @Test(groups = { "Functional" }, enabled = true)
198   public void testFindXrefSequences_indirectDbrefToNucleotide()
199   {
200     /*
201      * Alignment setup:
202      *   - peptide    dbref  UNIPROT|Q9ZTS2
203      *   - nucleotide dbref  EMBL|AF039662, UNIPROT|Q9ZTS2
204      */
205     SequenceI uniprotSeq = new Sequence("Q9ZTS2", "MASVSATMISTS");
206     uniprotSeq.addDBRef(new DBRefEntry("UNIPROT", "0", "Q9ZTS2"));
207     SequenceI emblSeq = new Sequence("AF039662", "GGGGCAGCACAAGAAC");
208     emblSeq.addDBRef(new DBRefEntry("EMBL", "0", "AF039662"));
209     emblSeq.addDBRef(new DBRefEntry("UNIPROT", "0", "Q9ZTS2"));
210
211     /*
212      * find EMBL xrefs for peptide sequence - it has no direct
213      * dbrefs, but the 'corresponding' nucleotide sequence does, so is returned
214      */
215     /*
216      * Find EMBL xrefs for peptide 
217      * - it has no EMBL dbref of its own
218      * - but nucleotide with matching peptide dbref does, so is returned
219      */
220     AlignmentI al = new Alignment(new SequenceI[] { emblSeq, uniprotSeq });
221     Alignment xrefs = new CrossRef(new SequenceI[] { uniprotSeq }, al)
222             .findXrefSequences("EMBL", false);
223     assertEquals(1, xrefs.getHeight());
224     assertSame(emblSeq, xrefs.getSequenceAt(0));
225   }
226
227   /**
228    * Test for finding 'product' sequences for the case where the selected
229    * sequence has no dbref to the desired source, and there are no indirect
230    * references via another sequence in the alignment
231    */
232   @Test(groups = { "Functional" })
233   public void testFindXrefSequences_noDbrefs()
234   {
235     /*
236      * two nucleotide sequences, one with UNIPROT dbref
237      */
238     SequenceI dna1 = new Sequence("AF039662", "GGGGCAGCACAAGAAC");
239     dna1.addDBRef(new DBRefEntry("UNIPROT", "0", "Q9ZTS2"));
240     SequenceI dna2 = new Sequence("AJ307031", "AAACCCTTT");
241
242     /*
243      * find UNIPROT xrefs for peptide sequence - it has no direct
244      * dbrefs, and the other sequence (which has a UNIPROT dbref) is not 
245      * equatable to it, so no results found
246      */
247     AlignmentI al = new Alignment(new SequenceI[] { dna1, dna2 });
248     Alignment xrefs = new CrossRef(new SequenceI[] { dna2 }, al)
249             .findXrefSequences("UNIPROT", true);
250     assertNull(xrefs);
251   }
252
253   /**
254    * Tests for the method that searches an alignment (with one sequence
255    * excluded) for protein/nucleotide sequences with a given cross-reference
256    */
257   @Test(groups = { "Functional" }, enabled = true)
258   public void testSearchDataset()
259   {
260     /*
261      * nucleotide sequence with UNIPROT AND EMBL dbref
262      * peptide sequence with UNIPROT dbref
263      */
264     SequenceI dna1 = new Sequence("AF039662", "GGGGCAGCACAAGAAC");
265     Mapping map = new Mapping(new Sequence("pep2", "MLAVSRG"),
266             new MapList(new int[]
267             { 1, 21 }, new int[] { 1, 7 }, 3, 1));
268     DBRefEntry dbref = new DBRefEntry("UNIPROT", "0", "Q9ZTS2", map);
269     dna1.addDBRef(dbref);
270     dna1.addDBRef(new DBRefEntry("EMBL", "0", "AF039662"));
271     SequenceI pep1 = new Sequence("Q9ZTS2", "MLAVSRGQ");
272     dbref = new DBRefEntry("UNIPROT", "0", "Q9ZTS2");
273     pep1.addDBRef(new DBRefEntry("UNIPROT", "0", "Q9ZTS2"));
274     AlignmentI al = new Alignment(new SequenceI[] { dna1, pep1 });
275
276     List<SequenceI> result = new ArrayList<>();
277
278     /*
279      * first search for a dbref nowhere on the alignment:
280      */
281     dbref = new DBRefEntry("UNIPROT", "0", "P30419");
282     CrossRef testee = new CrossRef(al.getSequencesArray(), al);
283     AlignedCodonFrame acf = new AlignedCodonFrame();
284     boolean found = testee.searchDataset(true, dna1, dbref, result, acf,
285             true, DBRefUtils.SEARCH_MODE_FULL);
286     assertFalse(found);
287     assertTrue(result.isEmpty());
288     assertTrue(acf.isEmpty());
289
290     /*
291      * search for a protein sequence with dbref UNIPROT:Q9ZTS2
292      */
293     acf = new AlignedCodonFrame();
294     dbref = new DBRefEntry("UNIPROT", "0", "Q9ZTS2");
295     found = testee.searchDataset(!dna1.isProtein(), dna1, dbref, result,
296             acf, false, DBRefUtils.SEARCH_MODE_FULL); // search dataset with a
297                                                       // protein xref from a dna
298     // sequence to locate the protein product
299     assertTrue(found);
300     assertEquals(1, result.size());
301     assertSame(pep1, result.get(0));
302     assertTrue(acf.isEmpty());
303
304     /*
305      * search for a nucleotide sequence with dbref UNIPROT:Q9ZTS2
306      */
307     result.clear();
308     acf = new AlignedCodonFrame();
309     dbref = new DBRefEntry("UNIPROT", "0", "Q9ZTS2");
310     found = testee.searchDataset(!pep1.isProtein(), pep1, dbref, result,
311             acf, false, DBRefUtils.SEARCH_MODE_FULL); // search dataset with a
312                                                       // protein's direct dbref
313                                                       // to
314     // locate dna sequences with matching xref
315     assertTrue(found);
316     assertEquals(1, result.size());
317     assertSame(dna1, result.get(0));
318     // should now have a mapping from dna to pep1
319     List<SequenceToSequenceMapping> mappings = acf.getMappings();
320     assertEquals(1, mappings.size());
321     SequenceToSequenceMapping mapping = mappings.get(0);
322     assertSame(dna1, mapping.getFromSeq());
323     assertSame(pep1, mapping.getMapping().getTo());
324     MapList mapList = mapping.getMapping().getMap();
325     assertEquals(1, mapList.getToRatio());
326     assertEquals(3, mapList.getFromRatio());
327     assertEquals(1, mapList.getFromRanges().size());
328     assertEquals(1, mapList.getFromRanges().get(0)[0]);
329     assertEquals(21, mapList.getFromRanges().get(0)[1]);
330     assertEquals(1, mapList.getToRanges().size());
331     assertEquals(1, mapList.getToRanges().get(0)[0]);
332     assertEquals(7, mapList.getToRanges().get(0)[1]);
333   }
334
335   /**
336    * Test for finding 'product' sequences for the case where the selected
337    * sequence has a dbref with a mapping to a sequence. This represents the case
338    * where either
339    * <ul>
340    * <li>a fetched sequence is already decorated with its cross-reference (e.g.
341    * EMBL + translation), or</li>
342    * <li>Get Cross-References has been done once resulting in instantiated
343    * cross-reference mappings</li>
344    * </ul>
345    */
346   @Test(groups = { "Functional" })
347   public void testFindXrefSequences_fromDbRefMap()
348   {
349     /*
350      * scenario: nucleotide sequence AF039662
351      *   with dbref + mapping to Q9ZTS2 and P30419
352      *     which themselves each have a dbref and feature
353      */
354     SequenceI dna1 = new Sequence("AF039662", "GGGGCAGCACAAGAAC");
355     SequenceI pep1 = new Sequence("Q9ZTS2", "MALFQRSV");
356     SequenceI pep2 = new Sequence("P30419", "MTRRSQIF");
357     dna1.createDatasetSequence();
358     pep1.createDatasetSequence();
359     pep2.createDatasetSequence();
360
361     pep1.getDatasetSequence()
362             .addDBRef(new DBRefEntry("Pfam", "0", "PF00111"));
363     pep1.addSequenceFeature(
364             new SequenceFeature("type", "desc", 12, 14, 1f, "group"));
365     pep2.getDatasetSequence().addDBRef(new DBRefEntry("PDB", "0", "3JTK"));
366     pep2.addSequenceFeature(
367             new SequenceFeature("type2", "desc2", 13, 15, 12f, "group2"));
368
369     MapList mapList = new MapList(new int[] { 1, 24 }, new int[] { 1, 3 },
370             3, 1);
371     Mapping map = new Mapping(pep1, mapList);
372     DBRefEntry dbRef1 = new DBRefEntry("UNIPROT", "0", "Q9ZTS2", map);
373     dna1.getDatasetSequence().addDBRef(dbRef1);
374     mapList = new MapList(new int[] { 1, 24 }, new int[] { 1, 3 }, 3, 1);
375     map = new Mapping(pep2, mapList);
376     DBRefEntry dbRef2 = new DBRefEntry("UNIPROT", "0", "P30419", map);
377     dna1.getDatasetSequence().addDBRef(dbRef2);
378
379     /*
380      * find UNIPROT xrefs for nucleotide sequence - it should pick up 
381      * mapped sequences
382      */
383     AlignmentI al = new Alignment(new SequenceI[] { dna1 });
384     Alignment xrefs = new CrossRef(new SequenceI[] { dna1 }, al)
385             .findXrefSequences("UNIPROT", true);
386     assertEquals(2, xrefs.getHeight());
387
388     /*
389      * cross-refs alignment holds copies of the mapped sequences
390      * including copies of their dbrefs and features
391      */
392     checkCopySequence(pep1, xrefs.getSequenceAt(0));
393     checkCopySequence(pep2, xrefs.getSequenceAt(1));
394   }
395
396   /**
397    * Helper method that verifies that 'copy' has the same name, start, end,
398    * sequence and dataset sequence object as 'original' (but is not the same
399    * object)
400    * 
401    * @param copy
402    * @param original
403    */
404   private void checkCopySequence(SequenceI copy, SequenceI original)
405   {
406     assertNotSame(copy, original);
407     assertSame(copy.getDatasetSequence(), original.getDatasetSequence());
408     assertEquals(copy.getName(), original.getName());
409     assertEquals(copy.getStart(), original.getStart());
410     assertEquals(copy.getEnd(), original.getEnd());
411     assertEquals(copy.getSequenceAsString(),
412             original.getSequenceAsString());
413   }
414
415   /**
416    * Test for finding 'product' sequences for the case where the selected
417    * sequence has a dbref with no mapping, triggering a fetch from database
418    */
419   @Test(groups = { "Functional_Failing" })
420   public void testFindXrefSequences_withFetch()
421   {
422     // JBPNote: this fails because pep1 and pep2 do not have DbRefEntrys with
423     // mappings
424     // Fix#1 would be to revise the test data so it fits with 2.11.2+ Jalview
425     // assumptions
426     // that ENA retrievals yield dbrefs with Mappings
427
428     SequenceI dna1 = new Sequence("AF039662", "GGGGCAGCACAAGAAC");
429     dna1.addDBRef(new DBRefEntry("UNIPROT", "ENA:0", "Q9ZTS2"));
430     dna1.addDBRef(new DBRefEntry("UNIPROT", "ENA:0", "P30419"));
431     dna1.addDBRef(new DBRefEntry("UNIPROT", "ENA:0", "P00314"));
432     final SequenceI pep1 = new Sequence("Q9ZTS2", "MYQLIRSSW");
433     pep1.addDBRef(new DBRefEntry("UNIPROT", "0", "Q9ZTS2", null, true));
434
435     final SequenceI pep2 = new Sequence("P00314", "MRKLLAASG");
436     pep2.addDBRef(new DBRefEntry("UNIPROT", "0", "P00314", null, true));
437
438     /*
439      * argument false suppresses adding DAS sources
440      * todo: define an interface type SequenceFetcherI and mock that
441      */
442     SequenceFetcher mockFetcher = new SequenceFetcher()
443     {
444       @Override
445       public boolean isFetchable(String source)
446       {
447         return true;
448       }
449
450       @Override
451       public SequenceI[] getSequences(List<DBRefEntry> refs, boolean dna)
452       {
453         return new SequenceI[] { pep1, pep2 };
454       }
455     };
456     SequenceFetcher.setMockFetcher(mockFetcher);
457
458     /*
459      * find UNIPROT xrefs for nucleotide sequence
460      */
461     AlignmentI al = new Alignment(new SequenceI[] { dna1 });
462     Alignment xrefs = new CrossRef(new SequenceI[] { dna1 }, al)
463             .findXrefSequences("UNIPROT", true);
464     assertEquals(2, xrefs.getHeight());
465     assertSame(pep1, xrefs.getSequenceAt(0));
466     assertSame(pep2, xrefs.getSequenceAt(1));
467   }
468
469   @AfterClass(alwaysRun = true)
470   public void tearDown()
471   {
472     SequenceFetcher.setMockFetcher(null);
473   }
474
475   /**
476    * Test for finding 'product' sequences for the case where both gene and
477    * transcript sequences have dbrefs to Uniprot.
478    */
479   @Test(groups = { "Functional_Failing" })
480   public void testFindXrefSequences_forGeneAndTranscripts()
481   {
482     /*
483      * 'gene' sequence
484      */
485     SequenceI gene = new Sequence("ENSG00000157764", "CGCCTCCCTTCCCC");
486     gene.addDBRef(new DBRefEntry("UNIPROT", "0", "P15056"));
487     gene.addDBRef(new DBRefEntry("UNIPROT", "0", "H7C5K3"));
488
489     /*
490      * 'transcript' with CDS feature (supports mapping to protein)
491      */
492     SequenceI braf001 = new Sequence("ENST00000288602",
493             "taagATGGCGGCGCTGa");
494     braf001.addDBRef(new DBRefEntry("UNIPROT", "0", "P15056"));
495     braf001.addSequenceFeature(
496             new SequenceFeature("CDS", "", 5, 16, 0f, null));
497
498     /*
499      * 'spliced transcript' with CDS ranges
500      */
501     SequenceI braf002 = new Sequence("ENST00000497784",
502             "gCAGGCtaTCTGTTCaa");
503     braf002.addDBRef(new DBRefEntry("UNIPROT", "ENSEMBL|0", "H7C5K3"));
504     braf002.addSequenceFeature(
505             new SequenceFeature("CDS", "", 2, 6, 0f, null));
506     braf002.addSequenceFeature(
507             new SequenceFeature("CDS", "", 9, 15, 0f, null));
508
509     /*
510      * TODO code is fragile - use of SequenceIdMatcher depends on fetched
511      * sequences having a name starting Source|Accession
512      * which happens to be true for Uniprot,PDB,EMBL but not Pfam,Rfam,Ensembl 
513      */
514     final SequenceI pep1 = new Sequence("UNIPROT|P15056", "MAAL");
515     pep1.addDBRef(new DBRefEntry("UNIPROT", "0", "P15056"));
516     final SequenceI pep2 = new Sequence("UNIPROT|H7C5K3", "QALF");
517     pep2.addDBRef(new DBRefEntry("UNIPROT", "0", "H7C5K3"));
518     /*
519      * argument false suppresses adding DAS sources
520      * todo: define an interface type SequenceFetcherI and mock that
521      */
522     SequenceFetcher mockFetcher = new SequenceFetcher()
523     {
524       @Override
525       public boolean isFetchable(String source)
526       {
527         return true;
528       }
529
530       @Override
531       public SequenceI[] getSequences(List<DBRefEntry> refs, boolean dna)
532       {
533         return new SequenceI[] { pep1, pep2 };
534       }
535     };
536     SequenceFetcher.setMockFetcher(mockFetcher);
537
538     /*
539      * find UNIPROT xrefs for gene and transcripts
540      * verify that
541      * - the two proteins are retrieved but not duplicated
542      * - mappings are built from transcript (CDS) to proteins
543      * - no mappings from gene to proteins
544      */
545     SequenceI[] seqs = new SequenceI[] { gene, braf001, braf002 };
546     AlignmentI al = new Alignment(seqs);
547     Alignment xrefs = new CrossRef(seqs, al).findXrefSequences("UNIPROT",
548             true);
549     assertEquals(2, xrefs.getHeight());
550     assertSame(pep1, xrefs.getSequenceAt(0));
551     assertSame(pep2, xrefs.getSequenceAt(1));
552   }
553
554   /**
555    * <pre>
556    * Test that emulates this (real but simplified) case:
557    * Alignment:          DBrefs
558    *     UNIPROT|P0CE19  EMBL|J03321, EMBL|X06707, EMBL|M19487
559    *     UNIPROT|P0CE20  EMBL|J03321, EMBL|X06707, EMBL|X07547
560    * Find cross-references for EMBL. These are mocked here as
561    *     EMBL|J03321     with mappings to P0CE18, P0CE19, P0CE20
562    *     EMBL|X06707     with mappings to P0CE17, P0CE19, P0CE20
563    *     EMBL|M19487     with mappings to P0CE19, Q46432
564    *     EMBL|X07547     with mappings to P0CE20, B0BCM4
565    * EMBL sequences are first 'fetched' (mocked here) for P0CE19.
566    * The 3 EMBL sequences are added to the alignment dataset.
567    * Their dbrefs to Uniprot products P0CE19 and P0CE20 should be matched in the
568    * alignment dataset and updated to reference the original Uniprot sequences.
569    * For the second Uniprot sequence, the J03321 and X06707 xrefs should be 
570    * resolved from the dataset, and only the X07547 dbref fetched.
571    * So the end state to verify is:
572    * - 4 cross-ref sequences returned: J03321, X06707,  M19487, X07547
573    * - P0CE19/20 dbrefs to EMBL sequences now have mappings
574    * - J03321 dbrefs to P0CE19/20 mapped to original Uniprot sequences
575    * - X06707 dbrefs to P0CE19/20 mapped to original Uniprot sequences
576    * </pre>
577    */
578   @Test(groups = { "Functional_Failing" })
579   public void testFindXrefSequences_uniprotEmblManyToMany()
580   {
581     /*
582      * Uniprot sequences, both with xrefs to EMBL|J03321 
583      * and EMBL|X07547
584      */
585     SequenceI p0ce19 = new Sequence("UNIPROT|P0CE19", "KPFG");
586     p0ce19.addDBRef(new DBRefEntry("EMBL", "0", "J03321"));
587     p0ce19.addDBRef(new DBRefEntry("EMBL", "0", "X06707"));
588     p0ce19.addDBRef(new DBRefEntry("EMBL", "0", "M19487"));
589     SequenceI p0ce20 = new Sequence("UNIPROT|P0CE20", "PFGK");
590     p0ce20.addDBRef(new DBRefEntry("EMBL", "0", "J03321"));
591     p0ce20.addDBRef(new DBRefEntry("EMBL", "0", "X06707"));
592     p0ce20.addDBRef(new DBRefEntry("EMBL", "0", "X07547"));
593
594     /*
595      * EMBL sequences to be 'fetched', complete with dbrefs and mappings
596      * to their protein products (CDS location  and translations  are provided
597      * in  EMBL XML); these should be matched to, and replaced with,
598      * the corresponding uniprot sequences after fetching
599      */
600
601     /*
602      * J03321 with mappings to P0CE19 and P0CE20
603      */
604     final SequenceI j03321 = new Sequence("EMBL|J03321",
605             "AAACCCTTTGGGAAAA");
606     DBRefEntry dbref1 = new DBRefEntry("UNIPROT", "0", "P0CE19");
607     MapList mapList = new MapList(new int[] { 1, 12 }, new int[] { 1, 4 },
608             3, 1);
609     Mapping map = new Mapping(new Sequence("UNIPROT|P0CE19", "KPFG"),
610             mapList);
611     // add a dbref to the mapped to sequence - should get copied to p0ce19
612     map.getTo().addDBRef(new DBRefEntry("PIR", "0", "S01875"));
613     dbref1.setMap(map);
614     j03321.addDBRef(dbref1);
615     DBRefEntry dbref2 = new DBRefEntry("UNIPROT", "0", "P0CE20");
616     mapList = new MapList(new int[] { 4, 15 }, new int[] { 2, 5 }, 3, 1);
617     dbref2.setMap(new Mapping(new Sequence("UNIPROT|P0CE20", "PFGK"),
618             new MapList(mapList)));
619     j03321.addDBRef(dbref2);
620
621     /*
622      * X06707 with mappings to P0CE19 and P0CE20
623      */
624     final SequenceI x06707 = new Sequence("EMBL|X06707", "atgAAACCCTTTGGG");
625     DBRefEntry dbref3 = new DBRefEntry("UNIPROT", "0", "P0CE19");
626     MapList map2 = new MapList(new int[] { 4, 15 }, new int[] { 1, 4 }, 3,
627             1);
628     dbref3.setMap(
629             new Mapping(new Sequence("UNIPROT|P0CE19", "KPFG"), map2));
630     x06707.addDBRef(dbref3);
631     DBRefEntry dbref4 = new DBRefEntry("UNIPROT", "0", "P0CE20");
632     MapList map3 = new MapList(new int[] { 4, 15 }, new int[] { 1, 4 }, 3,
633             1);
634     dbref4.setMap(
635             new Mapping(new Sequence("UNIPROT|P0CE20", "PFGK"), map3));
636     x06707.addDBRef(dbref4);
637
638     /*
639      * M19487 with mapping to P0CE19 and Q46432
640      */
641     final SequenceI m19487 = new Sequence("EMBL|M19487", "AAACCCTTTGGG");
642     DBRefEntry dbref5 = new DBRefEntry("UNIPROT", "0", "P0CE19");
643     dbref5.setMap(new Mapping(new Sequence("UNIPROT|P0CE19", "KPFG"),
644             new MapList(mapList)));
645     m19487.addDBRef(dbref5);
646     DBRefEntry dbref6 = new DBRefEntry("UNIPROT", "0", "Q46432");
647     dbref6.setMap(new Mapping(new Sequence("UNIPROT|Q46432", "KPFG"),
648             new MapList(mapList)));
649     m19487.addDBRef(dbref6);
650
651     /*
652      * X07547 with mapping to P0CE20 and B0BCM4
653      */
654     final SequenceI x07547 = new Sequence("EMBL|X07547", "cccAAACCCTTTGGG");
655     DBRefEntry dbref7 = new DBRefEntry("UNIPROT", "0", "P0CE20");
656     dbref7.setMap(new Mapping(new Sequence("UNIPROT|P0CE20", "PFGK"),
657             new MapList(map2)));
658     x07547.addDBRef(dbref7);
659     DBRefEntry dbref8 = new DBRefEntry("UNIPROT", "0", "B0BCM4");
660     dbref8.setMap(new Mapping(new Sequence("UNIPROT|B0BCM4", "KPFG"),
661             new MapList(map2)));
662     x07547.addDBRef(dbref8);
663
664     /*
665      * mock sequence fetcher to 'return' the EMBL sequences
666      * TODO: Mockito would allow .thenReturn().thenReturn() here, 
667      * and also capture and verification of the parameters
668      * passed in calls to getSequences() - important to verify that
669      * duplicate sequence fetches are not requested
670      */
671     SequenceFetcher mockFetcher = new SequenceFetcher()
672     {
673       int call = 0;
674
675       @Override
676       public boolean isFetchable(String source)
677       {
678         return true;
679       }
680
681       @Override
682       public SequenceI[] getSequences(List<DBRefEntry> refs, boolean dna)
683       {
684         call++;
685         if (call == 1)
686         {
687           assertEquals("Expected 3 embl seqs in first fetch", 3,
688                   refs.size());
689           return new SequenceI[] { j03321, x06707, m19487 };
690         }
691         else
692         {
693           assertEquals("Expected 1 embl seq in second fetch", 1,
694                   refs.size());
695           return new SequenceI[] { x07547 };
696         }
697       }
698     };
699     SequenceFetcher.setMockFetcher(mockFetcher);
700
701     /*
702      * find EMBL xrefs for Uniprot seqs and verify that
703      * - the EMBL xref'd sequences are retrieved without duplicates
704      * - mappings are added to the Uniprot dbrefs
705      * - mappings in the EMBL-to-Uniprot dbrefs are updated to the 
706      *   alignment sequences
707      * - dbrefs on the EMBL sequences are added to the original dbrefs
708      */
709     SequenceI[] seqs = new SequenceI[] { p0ce19, p0ce20 };
710     AlignmentI al = new Alignment(seqs);
711     Alignment xrefs = new CrossRef(seqs, al).findXrefSequences("EMBL",
712             false);
713
714     /*
715      * verify retrieved sequences
716      */
717     assertNotNull(xrefs);
718     assertEquals(4, xrefs.getHeight());
719     assertSame(j03321, xrefs.getSequenceAt(0));
720     assertSame(x06707, xrefs.getSequenceAt(1));
721     assertSame(m19487, xrefs.getSequenceAt(2));
722     assertSame(x07547, xrefs.getSequenceAt(3));
723
724     /*
725      * verify mappings added to Uniprot-to-EMBL dbrefs
726      */
727     Mapping mapping = p0ce19.getDBRefs().get(0).getMap();
728     assertSame(j03321, mapping.getTo());
729     mapping = p0ce19.getDBRefs().get(1).getMap();
730     assertSame(x06707, mapping.getTo());
731     mapping = p0ce20.getDBRefs().get(0).getMap();
732     assertSame(j03321, mapping.getTo());
733     mapping = p0ce20.getDBRefs().get(1).getMap();
734     assertSame(x06707, mapping.getTo());
735
736     /*
737      * verify dbrefs on EMBL are mapped to alignment seqs
738      */
739
740     assertSame(p0ce19, j03321.getDBRefs().get(0).getMap().getTo());
741     assertSame(p0ce20, j03321.getDBRefs().get(1).getMap().getTo());
742     assertSame(p0ce19, x06707.getDBRefs().get(0).getMap().getTo());
743     assertSame(p0ce20, x06707.getDBRefs().get(1).getMap().getTo());
744
745     /*
746      * verify new dbref on EMBL dbref mapping is copied to the
747      * original Uniprot sequence
748      */
749     assertEquals(4, p0ce19.getDBRefs().size());
750     assertEquals("PIR", p0ce19.getDBRefs().get(3).getSource());
751     assertEquals("S01875", p0ce19.getDBRefs().get(3).getAccessionId());
752   }
753
754   @Test(groups = "Functional")
755   public void testSameSequence()
756   {
757     assertTrue(CrossRef.sameSequence(null, null));
758     SequenceI seq1 = new Sequence("seq1", "ABCDEF");
759     assertFalse(CrossRef.sameSequence(seq1, null));
760     assertFalse(CrossRef.sameSequence(null, seq1));
761     assertTrue(CrossRef.sameSequence(seq1, new Sequence("seq2", "ABCDEF")));
762     assertTrue(CrossRef.sameSequence(seq1, new Sequence("seq2", "abcdef")));
763     assertFalse(
764             CrossRef.sameSequence(seq1, new Sequence("seq2", "ABCDE-F")));
765     assertFalse(CrossRef.sameSequence(seq1, new Sequence("seq2", "BCDEF")));
766   }
767 }