JAL-3536 failing test and test data
[jalview.git] / test / jalview / io / CrossRef2xmlTests.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.io;
22
23 import static org.testng.Assert.assertEquals;
24 import static org.testng.Assert.assertNotNull;
25 import static org.testng.Assert.assertTrue;
26
27 import jalview.analysis.CrossRef;
28 import jalview.api.AlignmentViewPanel;
29 import jalview.datamodel.AlignedCodonFrame;
30 import jalview.datamodel.AlignmentI;
31 import jalview.datamodel.AlignmentTest;
32 import jalview.datamodel.SequenceI;
33 import jalview.gui.AlignFrame;
34 import jalview.gui.CrossRefAction;
35 import jalview.gui.Desktop;
36 import jalview.gui.JvOptionPane;
37 import jalview.project.Jalview2XML;
38 import jalview.util.DBRefUtils;
39
40 import java.io.File;
41 import java.io.IOException;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.HashMap;
45 import java.util.List;
46 import java.util.Map;
47
48 import org.testng.Assert;
49 import org.testng.annotations.BeforeClass;
50 import org.testng.annotations.DataProvider;
51 import org.testng.annotations.Test;
52
53 import junit.extensions.PA;
54
55 @Test(singleThreaded = true)
56 public class CrossRef2xmlTests extends Jalview2xmlBase
57 {
58
59   @Override
60   @BeforeClass(alwaysRun = true)
61   public void setUpJvOptionPane()
62   {
63     JvOptionPane.setInteractiveMode(false);
64     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
65   }
66
67   @Test(groups = { "Functional" }, enabled = true)
68   public void openCrossrefsForEnsemblTwice()
69   {
70     AlignFrame af = new FileLoader(false).LoadFileWaitTillLoaded(
71             "examples/testdata/CantShowEnsemblCrossrefsTwice.jvp",
72             DataSourceType.FILE);
73     assertNotNull(af, "Couldn't load test's project.");
74     AlignmentI origAlig = af.getViewport().getAlignment();
75     List<String> source = new CrossRef(origAlig.getSequencesArray(),
76             origAlig.getDataset()).findXrefSourcesForSequences(true);
77     assertEquals(source.size(), 1, "Expected just one crossref to show.");
78     List<AlignmentViewPanel> views;
79     {
80       // try to show once - in a code block so handler is forgotten about
81       CrossRefAction xref1 = CrossRefAction.getHandlerFor(
82               origAlig.getSequencesArray(), true, source.get(0), af);
83       try
84       {
85         xref1.run();
86         views = (List<AlignmentViewPanel>) PA.getValue(xref1, "xrefViews");
87         assertTrue(views.size() > 0,
88                 "Couldn't get cross ref on first attempt (SERIOUS FAIL).");
89       } catch (Exception ex)
90       {
91         Assert.fail("Unexpected Exception for first xref action", ex);
92       }
93     }
94
95     views = null;
96     // now just try it again
97     CrossRefAction xref2 = CrossRefAction.getHandlerFor(
98             origAlig.getSequencesArray(), true, source.get(0), af);
99     try
100     {
101       xref2.run();
102       views = (List<AlignmentViewPanel>) PA.getValue(xref2, "xrefViews");
103       assertTrue(views.size() > 0,
104               "Couldn't get cross ref on second attempt (SERIOUS FAIL).");
105     } catch (Exception ex)
106     {
107       Assert.fail("Unexpected Exception for second xref action", ex);
108     }
109     // TODO : check that both views contain the same data
110   }
111
112   @DataProvider(name = "initialAccessions")
113   static Object[][] getAccessions()
114   {
115     return new String[][] { { "UNIPROT", "P00338" },
116         { "UNIPROT", "Q8Z9G6" },
117         { "ENSEMBLGENOMES", "CAD01290" } };
118   }
119
120   /**
121    * test store and recovery of all reachable cross refs from all reachable
122    * crossrefs for one or more fetched db refs. Currently, this test has a known
123    * failure case.
124    * 
125    * @throws Exception
126    */
127   @Test(
128     groups =
129     { "Operational" },
130     dataProvider = "initialAccessions",
131     enabled = true)
132   public void testRetrieveAndShowCrossref(String forSource,
133           String forAccession) throws Exception
134   {
135
136     List<String> failedDBRetr = new ArrayList<>();
137     List<String> failedXrefMenuItems = new ArrayList<>();
138     List<String> failedProjectRecoveries = new ArrayList<>();
139     // only search for ensembl or Uniprot crossrefs
140     List<String> limit=Arrays.asList(new String[] {
141         DBRefUtils.getCanonicalName("ENSEMBL"), 
142         DBRefUtils.getCanonicalName("Uniprot")});
143     // for every set of db queries
144     // retrieve db query
145     // verify presence of expected xrefs
146     // show xrefs - verify expected type of frame is shown for each xref
147     // show xrefs again
148     // - verify original -> xref -> xref(original) recovers frame containing at
149     // least the first retrieved sequence
150     // store
151     // 1. whole project
152     // 2. individual frames
153     // 3. load each one back and verify
154     // . aligned sequences (.toString() )
155     // . xrefs (.toString() )
156     // . codonframes
157     //
158     //
159     Map<String, String> dbtoviewBit = new HashMap<>();
160     List<String> keyseq = new ArrayList<>();
161     Map<String, File> savedProjects = new HashMap<>();
162
163 //    for (String[] did : new String[][] { { "UNIPROT", "P00338" } })
164 //    {
165       // pass counters - 0 - first pass, 1 means retrieve project rather than
166       // perform action
167       int pass1 = 0, pass2 = 0, pass3 = 0;
168       // each do loop performs two iterations in the first outer loop pass, but
169       // only performs one iteration on the second outer loop
170       // ie. pass 1 = 0 {pass 2= 0 { pass 3 = 0,1 }, pass 2=1 { pass 3 = 0 }}, 1
171       // { pass 2 = 0 { pass 3 = 0 } }
172       do
173       {
174         String first = forSource + " " + forAccession;//did[0] + " " + did[1];
175         AlignFrame af = null;
176         boolean dna;
177         AlignmentI retral;
178         AlignmentI dataset;
179         SequenceI[] seqs;
180         List<String> ptypes = null;
181         if (pass1 == 0)
182         {
183           // retrieve dbref
184
185           List<AlignFrame> afs = jalview.gui.SequenceFetcher.fetchAndShow(
186                 forSource, forAccession);
187         // did[0], did[1]);
188           if (afs.size() == 0)
189           {
190             failedDBRetr.add("Didn't retrieve " + first);
191             break;
192           }
193           keyseq.add(first);
194           af = afs.get(0);
195
196           // verify references for retrieved data
197           AlignmentTest.assertAlignmentDatasetRefs(af.getViewport()
198                   .getAlignment(), "Pass (" + pass1 + "," + pass2 + ","
199                   + pass3 + "): Fetch " + first + ":");
200           assertDatasetIsNormalisedKnownDefect(af.getViewport()
201                   .getAlignment(), "Pass (" + pass1 + "," + pass2 + ","
202                   + pass3 + "): Fetch " + first + ":");
203           dna = af.getViewport().getAlignment().isNucleotide();
204           retral = af.getViewport().getAlignment();
205           dataset = retral.getDataset();
206           seqs = retral.getSequencesArray();
207
208         }
209         else
210         {
211           Desktop.instance.closeAll_actionPerformed(null);
212           // recover stored project
213           af = new FileLoader(false).LoadFileWaitTillLoaded(savedProjects
214                   .get(first).toString(), DataSourceType.FILE);
215           System.out.println("Recovered view for '" + first + "' from '"
216                   + savedProjects.get(first).toString() + "'");
217           dna = af.getViewport().getAlignment().isNucleotide();
218           retral = af.getViewport().getAlignment();
219           dataset = retral.getDataset();
220           seqs = retral.getSequencesArray();
221
222           // verify references for recovered data
223           AlignmentTest.assertAlignmentDatasetRefs(af.getViewport()
224                   .getAlignment(), "Pass (" + pass1 + "," + pass2 + ","
225                   + pass3 + "): Recover " + first + ":");
226           assertDatasetIsNormalisedKnownDefect(af.getViewport()
227                   .getAlignment(), "Pass (" + pass1 + "," + pass2 + ","
228                   + pass3 + "): Recover " + first + ":");
229
230         }
231
232         // store project on first pass, compare next pass
233         stringify(dbtoviewBit, savedProjects, first, af.alignPanel);
234
235         ptypes = (seqs == null || seqs.length == 0) ? null : new CrossRef(
236                 seqs, dataset).findXrefSourcesForSequences(dna);
237         filterDbRefs(ptypes, limit);
238         
239         // start of pass2: retrieve each cross-ref for fetched or restored
240         // project.
241         do // first cross ref and recover crossref loop
242         {
243
244           for (String db : ptypes)
245           {
246             // counter for splitframe views retrieved via crossref
247             int firstcr_ap = 0;
248             // build next key so we an retrieve all views
249             String nextxref = first + " -> " + db + "{" + firstcr_ap + "}";
250             // perform crossref action, or retrieve stored project
251             List<AlignmentViewPanel> cra_views = new ArrayList<>();
252             CrossRefAction cra = null;
253
254             if (pass2 == 0)
255             { // retrieve and show cross-refs in this thread
256               cra = CrossRefAction.getHandlerFor(seqs, dna, db, af);
257               cra.run();
258               cra_views = (List<AlignmentViewPanel>) PA.getValue(cra,
259                       "xrefViews");
260               if (cra_views.size() == 0)
261               {
262                 failedXrefMenuItems.add("No crossrefs retrieved for "
263                         + first + " -> " + db);
264                 continue;
265               }
266               assertNucleotide(cra_views.get(0),
267                       "Nucleotide panel included proteins for " + first
268                               + " -> " + db);
269               assertProtein(cra_views.get(1),
270                       "Protein panel included nucleotides for " + first
271                               + " -> " + db);
272             }
273             else
274             {
275               Desktop.instance.closeAll_actionPerformed(null);
276               pass3 = 0;
277               // recover stored project
278               File storedProject = savedProjects.get(nextxref);
279               if (storedProject == null)
280               {
281                 failedProjectRecoveries.add("Failed to store a view for '"
282                         + nextxref + "'");
283                 continue;
284               }
285
286               // recover stored project
287               AlignFrame af2 = new FileLoader(false)
288                       .LoadFileWaitTillLoaded(savedProjects.get(nextxref)
289                               .toString(), DataSourceType.FILE);
290               System.out.println("Recovered view for '" + nextxref
291                       + "' from '" + savedProjects.get(nextxref).toString()
292                       + "'");
293               // gymnastics to recover the alignPanel/Complementary alignPanel
294               if (af2.getViewport().isNucleotide())
295               {
296                 // top view, then bottom
297                 cra_views.add(af2.getViewport().getAlignPanel());
298                 cra_views.add(((jalview.gui.AlignViewport) af2
299                         .getViewport().getCodingComplement())
300                         .getAlignPanel());
301
302               }
303               else
304               {
305                 // bottom view, then top
306                 cra_views.add(((jalview.gui.AlignViewport) af2
307                         .getViewport().getCodingComplement())
308                         .getAlignPanel());
309                 cra_views.add(af2.getViewport().getAlignPanel());
310
311               }
312             }
313             HashMap<String, List<String>> xrptypes = new HashMap<>();
314             // first save/verify views.
315             for (AlignmentViewPanel avp : cra_views)
316             {
317               nextxref = first + " -> " + db + "{" + firstcr_ap++ + "}";
318               // verify references for this panel
319               AlignmentTest.assertAlignmentDatasetRefs(avp.getAlignment(),
320                       "Pass (" + pass1 + "," + pass2 + "," + pass3
321                               + "): before start of pass3: " + nextxref
322                               + ":");
323               assertDatasetIsNormalisedKnownDefect(avp.getAlignment(),
324                       "Pass (" + pass1 + "," + pass2 + "," + pass3
325                               + "): before start of pass3: " + nextxref
326                               + ":");
327
328               SequenceI[] xrseqs = avp.getAlignment().getSequencesArray();
329
330               List<String> _xrptypes = (seqs == null || seqs.length == 0) ? null
331                       : new CrossRef(xrseqs, dataset)
332                               .findXrefSourcesForSequences(avp
333                                       .getAlignViewport().isNucleotide());
334
335               stringify(dbtoviewBit, savedProjects, nextxref, avp);
336               xrptypes.put(nextxref, _xrptypes);
337
338             }
339
340             // now do the second xref pass starting from either saved or just
341             // recovered split pane, in sequence
342             do // retrieve second set of cross refs or recover and verify
343             {
344               firstcr_ap = 0;
345               for (AlignmentViewPanel avp : cra_views)
346               {
347                 nextxref = first + " -> " + db + "{" + firstcr_ap++ + "}";
348                 for (String xrefdb : xrptypes.get(nextxref))
349                 {
350                   List<AlignmentViewPanel> cra_views2 = new ArrayList<>();
351                   int q = 0;
352                   String nextnextxref = nextxref + " -> " + xrefdb + "{"
353                           + q + "}";
354
355                   if (pass3 == 0)
356                   {
357                     SequenceI[] xrseqs = avp.getAlignment()
358                             .getSequencesArray();
359                     AlignFrame nextaf = Desktop.getAlignFrameFor(avp
360                             .getAlignViewport());
361
362                     cra = CrossRefAction.getHandlerFor(xrseqs, avp
363                             .getAlignViewport().isNucleotide(), xrefdb,
364                             nextaf);
365                     cra.run();
366                     cra_views2 = (List<AlignmentViewPanel>) PA.getValue(
367                             cra, "xrefViews");
368                     if (cra_views2.size() == 0)
369                     {
370                       failedXrefMenuItems
371                               .add("No crossrefs retrieved for '"
372                                       + nextxref + "' to " + xrefdb
373                                       + " via '" + nextaf.getTitle() + "'");
374                       continue;
375                     }
376                     assertNucleotide(cra_views2.get(0),
377                             "Nucleotide panel included proteins for '"
378                                     + nextxref + "' to " + xrefdb
379                                     + " via '" + nextaf.getTitle() + "'");
380                     assertProtein(cra_views2.get(1),
381                             "Protein panel included nucleotides for '"
382                                     + nextxref + "' to " + xrefdb
383                                     + " via '" + nextaf.getTitle() + "'");
384
385                   }
386                   else
387                   {
388                     Desktop.instance.closeAll_actionPerformed(null);
389                     // recover stored project
390                     File storedProject = savedProjects.get(nextnextxref);
391                     if (storedProject == null)
392                     {
393                       failedProjectRecoveries
394                               .add("Failed to store a view for '"
395                                       + nextnextxref + "'");
396                       continue;
397                     }
398                     AlignFrame af2 = new FileLoader(false)
399                             .LoadFileWaitTillLoaded(
400                                     savedProjects.get(nextnextxref)
401                                             .toString(),
402                                     DataSourceType.FILE);
403                     System.out.println("Recovered view for '"
404                             + nextnextxref + "' from '"
405                             + savedProjects.get(nextnextxref).toString()
406                             + "'");
407                     // gymnastics to recover the alignPanel/Complementary
408                     // alignPanel
409                     if (af2.getViewport().isNucleotide())
410                     {
411                       // top view, then bottom
412                       cra_views2.add(af2.getViewport().getAlignPanel());
413                       cra_views2.add(((jalview.gui.AlignViewport) af2
414                               .getViewport().getCodingComplement())
415                               .getAlignPanel());
416
417                     }
418                     else
419                     {
420                       // bottom view, then top
421                       cra_views2.add(((jalview.gui.AlignViewport) af2
422                               .getViewport().getCodingComplement())
423                               .getAlignPanel());
424                       cra_views2.add(af2.getViewport().getAlignPanel());
425                     }
426                     Assert.assertEquals(cra_views2.size(), 2);
427                     Assert.assertNotNull(cra_views2.get(0));
428                     Assert.assertNotNull(cra_views2.get(1));
429                   }
430
431                   for (AlignmentViewPanel nextavp : cra_views2)
432                   {
433                     nextnextxref = nextxref + " -> " + xrefdb + "{" + q++
434                             + "}";
435
436                     // verify references for this panel
437                     AlignmentTest.assertAlignmentDatasetRefs(
438                             nextavp.getAlignment(), "" + "Pass (" + pass1
439                                     + "," + pass2 + "): For "
440                                     + nextnextxref + ":");
441                     assertDatasetIsNormalisedKnownDefect(
442                             nextavp.getAlignment(), "" + "Pass (" + pass1
443                                     + "," + pass2 + "): For "
444                                     + nextnextxref + ":");
445
446                     stringify(dbtoviewBit, savedProjects, nextnextxref,
447                             nextavp);
448                     keyseq.add(nextnextxref);
449                   }
450                 } // end of loop around showing all xrefdb for crossrf2
451
452               } // end of loop around all viewpanels from crossrf1
453             } while (pass2 == 2 && pass3++ < 2);
454             // fetchdb->crossref1->crossref-2->verify for xrefs we
455             // either loop twice when pass2=0, or just once when pass2=1
456             // (recovered project from previous crossref)
457
458           } // end of loop over db-xrefs for crossref-2
459
460           // fetchdb-->crossref1
461           // for each xref we try to retrieve xref, store and verify when
462           // pass1=0, or just retrieve and verify when pass1=1
463         } while (pass1 == 1 && pass2++ < 2);
464         // fetchdb
465         // for each ref we
466         // loop twice: first, do the retrieve, second recover from saved project
467
468         // increment pass counters, so we repeat traversal starting from the
469         // oldest saved project first.
470         if (pass1 == 0)
471         {
472           // verify stored projects for first set of cross references
473           pass1 = 1;
474           // and verify cross-references retrieved from stored projects
475           pass2 = 0;
476           pass3 = 0;
477         }
478         else
479         {
480           pass1++;
481         }
482       } while (pass1 < 3);
483
484     if (failedXrefMenuItems.size() > 0)
485     {
486       for (String s : failedXrefMenuItems)
487       {
488         System.err.println(s);
489       }
490       Assert.fail("Faulty xref menu (" + failedXrefMenuItems.size()
491               + " counts)");
492     }
493     if (failedProjectRecoveries.size() > 0)
494     {
495
496       for (String s : failedProjectRecoveries)
497       {
498         System.err.println(s);
499       }
500       Assert.fail("Didn't recover projects for some retrievals (did they retrieve ?) ("
501               + failedProjectRecoveries.size() + " counts)");
502     }
503     if (failedDBRetr.size() > 0)
504     {
505       for (String s : failedProjectRecoveries)
506       {
507         System.err.println(s);
508       }
509       Assert.fail("Didn't retrieve some db refs for checking cross-refs ("
510               + failedDBRetr.size() + " counts)");
511     }
512   }
513
514   private void filterDbRefs(List<String> ptypes, List<String> limit)
515   {
516     if (limit != null)
517     {
518       int p = 0;
519       while (ptypes.size() > p)
520       {
521         if (!limit.contains(ptypes.get(p)))
522         {
523           ptypes.remove(p);
524         }
525         else
526         {
527           p++;
528         }
529       }
530     }
531   }
532
533   /**
534    * wrapper to trap known defect for AH002001 testcase
535    * 
536    * @param alignment
537    * @param string
538    */
539   private void assertDatasetIsNormalisedKnownDefect(AlignmentI al,
540           String message)
541   {
542     try
543     {
544       AlignmentTest.assertDatasetIsNormalised(al, message);
545     } catch (AssertionError ae)
546     {
547       if (!ae.getMessage().endsWith("EMBL|AH002001"))
548       {
549         throw ae;
550       }
551       else
552       {
553         System.out
554                 .println("Ignored exception for known defect: JAL-2179 : "
555                         + message);
556       }
557
558     }
559   }
560
561   private void assertProtein(AlignmentViewPanel alignmentViewPanel,
562           String message)
563   {
564     assertType(true, alignmentViewPanel, message);
565   }
566
567   private void assertNucleotide(AlignmentViewPanel alignmentViewPanel,
568           String message)
569   {
570     assertType(false, alignmentViewPanel, message);
571   }
572
573   private void assertType(boolean expectProtein,
574           AlignmentViewPanel alignmentViewPanel, String message)
575   {
576     List<SequenceI> nonType = new ArrayList<>();
577     for (SequenceI sq : alignmentViewPanel.getAlignViewport()
578             .getAlignment().getSequences())
579     {
580       if (sq.isProtein() != expectProtein)
581       {
582         nonType.add(sq);
583       }
584     }
585     if (nonType.size() > 0)
586     {
587       Assert.fail(message + " [ "
588               + (expectProtein ? "nucleotides were " : "proteins were ")
589               + nonType.toString() + " ]");
590     }
591   }
592
593   /**
594    * first time called, record strings derived from alignment and
595    * alignedcodonframes, and save view to a project file. Second time called,
596    * compare strings to existing ones. org.testng.Assert.assertTrue on
597    * stringmatch
598    * 
599    * @param dbtoviewBit
600    *          map between xrefpath and view string
601    * @param savedProjects
602    *          - map from xrefpath to saved project filename (createTempFile)
603    * @param xrefpath
604    *          - xrefpath - unique ID for this context (composed of sequence of
605    *          db-fetch/cross-ref actions preceeding state)
606    * @param avp
607    *          - viewpanel to store (for viewpanels in splitframe, the same
608    *          project should be written for both panels, only one needs
609    *          recovering for comparison on the next stringify call, but each
610    *          viewpanel needs to be called with a distinct xrefpath to ensure
611    *          each one's strings are compared)
612    */
613   private void stringify(Map<String, String> dbtoviewBit,
614           Map<String, File> savedProjects, String xrefpath,
615           AlignmentViewPanel avp)
616   {
617     if (savedProjects != null)
618     {
619       if (savedProjects.get(xrefpath) == null)
620       {
621         // write a project file for this view. On the second pass, this will be
622         // recovered and cross-references verified
623         try
624         {
625           File prfile = File.createTempFile("crossRefTest", ".jvp");
626           AlignFrame af = Desktop.getAlignFrameFor(avp.getAlignViewport());
627           new Jalview2XML(false).saveAlignment(af, prfile.toString(),
628                   af.getTitle());
629           System.out.println("Written view from '" + xrefpath + "' as '"
630                   + prfile.getAbsolutePath() + "'");
631           savedProjects.put(xrefpath, prfile);
632         } catch (IOException q)
633         {
634           Assert.fail("Unexpected IO Exception", q);
635         }
636       }
637       else
638       {
639         System.out.println("Stringify check on view from '" + xrefpath
640                 + "' [ possibly retrieved from '"
641                 + savedProjects.get(xrefpath).getAbsolutePath() + "' ]");
642
643       }
644     }
645
646     StringBuilder sbr = new StringBuilder();
647     sbr.append(avp.getAlignment().toString());
648     sbr.append("\n");
649     sbr.append("<End of alignment>");
650     sbr.append("\n");
651     sbr.append(avp.getAlignment().getDataset());
652     sbr.append("\n");
653     sbr.append("<End of dataset>");
654     sbr.append("\n");
655     int p = 0;
656     if (avp.getAlignment().getCodonFrames() != null)
657     {
658       for (AlignedCodonFrame ac : avp.getAlignment().getCodonFrames())
659       {
660         sbr.append("<AlignedCodonFrame " + p++ + ">");
661         sbr.append("\n");
662         sbr.append(ac.toString());
663         sbr.append("\n");
664       }
665     }
666     String dbt = dbtoviewBit.get(xrefpath);
667     if (dbt == null)
668     {
669       dbtoviewBit.put(xrefpath, sbr.toString());
670     }
671     else
672     {
673       Assert.assertEquals(sbr.toString(), dbt, "stringify mismatch for "
674               + xrefpath);
675     }
676   }
677 }