JAL-3048 update tests for alignment export bean refactoring
[jalview.git] / test / jalview / io / JSONFileTest.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.AssertJUnit.assertNotNull;
24
25 import jalview.api.AlignExportSettingI;
26 import jalview.datamodel.Alignment;
27 import jalview.datamodel.AlignmentAnnotation;
28 import jalview.datamodel.AlignmentExportData;
29 import jalview.datamodel.AlignmentI;
30 import jalview.datamodel.Annotation;
31 import jalview.datamodel.HiddenColumns;
32 import jalview.datamodel.Sequence;
33 import jalview.datamodel.SequenceFeature;
34 import jalview.datamodel.SequenceGroup;
35 import jalview.datamodel.SequenceI;
36 import jalview.datamodel.features.SequenceFeatures;
37 import jalview.gui.AlignFrame;
38 import jalview.gui.JvOptionPane;
39 import jalview.json.binding.biojson.v1.ColourSchemeMapper;
40 import jalview.schemes.ColourSchemeI;
41 import jalview.schemes.ResidueColourScheme;
42
43 import java.awt.event.ActionListener;
44 import java.io.IOException;
45 import java.util.ArrayList;
46 import java.util.HashMap;
47 import java.util.Iterator;
48 import java.util.List;
49 import java.util.Map;
50
51 import org.testng.Assert;
52 import org.testng.AssertJUnit;
53 import org.testng.annotations.AfterTest;
54 import org.testng.annotations.BeforeClass;
55 import org.testng.annotations.BeforeMethod;
56 import org.testng.annotations.BeforeTest;
57 import org.testng.annotations.Test;
58
59 public class JSONFileTest
60 {
61
62   @BeforeClass(alwaysRun = true)
63   public void setUpJvOptionPane()
64   {
65     JvOptionPane.setInteractiveMode(false);
66     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
67   }
68
69   private int TEST_SEQ_HEIGHT = 0;
70
71   private int TEST_GRP_HEIGHT = 0;
72
73   private int TEST_ANOT_HEIGHT = 0;
74
75   private int TEST_CS_HEIGHT = 0;
76
77   private String TEST_JSON_FILE = "examples/example.json";
78
79   private Alignment alignment;
80
81   private HashMap<String, SequenceI> expectedSeqs = new HashMap<>();
82
83   private HashMap<String, AlignmentAnnotation> expectedAnnots = new HashMap<>();
84
85   private HashMap<String, SequenceGroup> expectedGrps = new HashMap<>();
86
87   private HiddenColumns expectedColSel = new HiddenColumns();
88
89   private SequenceI[] expectedHiddenSeqs = new SequenceI[1];
90
91   private AlignmentI testAlignment;
92
93   private int passedCount;
94
95   private JSONFile testJsonFile;
96
97   private JSONFile jf;
98
99   private AlignExportSettingI exportSettings;
100
101   @BeforeTest(alwaysRun = true)
102   public void setup() throws Exception
103   {
104     /*
105      * construct expected values
106      * nb this have to match the data in examples/example.json
107      */
108     // create and add sequences
109     Sequence[] seqs = new Sequence[5];
110     seqs[0] = new Sequence("FER_CAPAN",
111             "SVSATMISTSFMPRKPAVTSL-KPIPNVGE--ALF", 3, 34);
112     seqs[1] = new Sequence("FER1_SOLLC",
113             "SISGTMISTSFLPRKPAVTSL-KAISNVGE--ALF", 3, 34);
114     seqs[2] = new Sequence("Q93XJ9_SOLTU",
115             "SISGTMISTSFLPRKPVVTSL-KAISNVGE--ALF", 3, 34);
116     seqs[3] = new Sequence("FER1_PEA",
117             "ALYGTAVSTSFLRTQPMPMSV-TTTKAFSN--GFL", 6, 37);
118     seqs[4] = new Sequence("Q7XA98_TRIPR",
119             "ALYGTAVSTSFMRRQPVPMSV-ATTTTTKAFPSGF", 6, 39);
120
121     SequenceI hiddenSeq = new Sequence("FER_TOCH",
122             "FILGTMISKSFLFRKPAVTSL-KAISNVGE--ALF", 3, 34);
123     expectedHiddenSeqs[0] = hiddenSeq;
124
125     // create and add sequence features
126     SequenceFeature seqFeature2 = new SequenceFeature("feature_x",
127             "theDesc", 6, 15, "Jalview");
128     SequenceFeature seqFeature3 = new SequenceFeature("feature_x",
129             "theDesc", 9, 18, "Jalview");
130     SequenceFeature seqFeature4 = new SequenceFeature("feature_x",
131             "theDesc", 9, 18, "Jalview");
132     // non-positional feature:
133     SequenceFeature seqFeature5 = new SequenceFeature("Domain",
134             "My description", 0, 0, "Pfam");
135     seqs[2].addSequenceFeature(seqFeature2);
136     seqs[3].addSequenceFeature(seqFeature3);
137     seqs[4].addSequenceFeature(seqFeature4);
138     seqs[2].addSequenceFeature(seqFeature5);
139
140     for (Sequence seq : seqs)
141     {
142       seq.createDatasetSequence();
143       expectedSeqs.put(seq.getName(), seq);
144     }
145
146     // create and add a sequence group
147     List<SequenceI> grpSeqs = new ArrayList<>();
148     grpSeqs.add(seqs[1]);
149     grpSeqs.add(seqs[2]);
150     grpSeqs.add(seqs[3]);
151     grpSeqs.add(seqs[4]);
152     SequenceGroup seqGrp = new SequenceGroup(grpSeqs,
153             "JGroup:1883305585",
154             null, true, true, false, 21, 29);
155     ColourSchemeI scheme = ColourSchemeMapper.getJalviewColourScheme(
156             "zappo", seqGrp);
157     seqGrp.cs.setColourScheme(scheme);
158     seqGrp.setShowNonconserved(false);
159     seqGrp.setDescription(null);
160
161     expectedGrps.put(seqGrp.getName(), seqGrp);
162
163     // create and add annotation
164     Annotation[] annot = new Annotation[35];
165     annot[0] = new Annotation("", "", '\u0000', 0);
166     annot[1] = new Annotation("", "", '\u0000', 0);
167     annot[2] = new Annotation("α", "", 'H', 0);
168     annot[3] = new Annotation("α", "", 'H', 0);
169     annot[4] = new Annotation("α", "", 'H', 0);
170     annot[5] = new Annotation("", "", '\u0000', 0);
171     annot[6] = new Annotation("", "", '\u0000', 0);
172     annot[7] = new Annotation("", "", '\u0000', 0);
173     annot[8] = new Annotation("β", "", 'E', 0);
174     annot[9] = new Annotation("β", "", 'E', 0);
175     annot[10] = new Annotation("β", "", 'E', 0);
176     annot[11] = new Annotation("β", "", 'E', 0);
177     annot[12] = new Annotation("β", "", 'E', 0);
178     annot[13] = new Annotation("β", "", 'E', 0);
179     annot[14] = new Annotation("β", "", 'E', 0);
180     annot[15] = new Annotation("β", "", 'E', 0);
181     annot[16] = new Annotation("", "", '\u0000', 0);
182     annot[17] = new Annotation("", "", '\u0000', 0);
183     annot[18] = new Annotation("", "", '\u0000', 0);
184     annot[19] = new Annotation("", "", '\u0000', 0);
185     annot[20] = new Annotation("", "", '\u0000', 0);
186     annot[21] = new Annotation("", "", '\u0000', 0);
187     annot[22] = new Annotation("", "", '\u0000', 0);
188     annot[23] = new Annotation("", "", '\u0000', 0);
189     annot[24] = new Annotation("", "", '\u0000', 0);
190     annot[25] = new Annotation("", "", '\u0000', 0);
191     annot[26] = new Annotation("α", "", 'H', 0);
192     annot[27] = new Annotation("α", "", 'H', 0);
193     annot[28] = new Annotation("α", "", 'H', 0);
194     annot[29] = new Annotation("α", "", 'H', 0);
195     annot[30] = new Annotation("α", "", 'H', 0);
196     annot[31] = new Annotation("", "", '\u0000', 0);
197     annot[32] = new Annotation("", "", '\u0000', 0);
198     annot[33] = new Annotation("", "", '\u0000', 0);
199     annot[34] = new Annotation("", "", '\u0000', 0);
200
201     AlignmentAnnotation alignAnnot = new AlignmentAnnotation(
202             "Secondary Structure", "New description", annot);
203     expectedAnnots.put(alignAnnot.label, alignAnnot);
204
205     expectedColSel.hideColumns(32, 33);
206     expectedColSel.hideColumns(34, 34);
207
208     TEST_SEQ_HEIGHT = expectedSeqs.size();
209     TEST_GRP_HEIGHT = expectedGrps.size();
210     TEST_ANOT_HEIGHT = expectedAnnots.size();
211     TEST_CS_HEIGHT = expectedColSel.getNumberOfRegions();
212
213     exportSettings = new AlignExportSettingI()
214     {
215       @Override
216       public boolean isExportHiddenSequences()
217       {
218         return true;
219       }
220
221       @Override
222       public boolean isExportHiddenColumns()
223       {
224         return true;
225       }
226
227       @Override
228       public boolean isExportGroups()
229       {
230         return true;
231       }
232
233       @Override
234       public boolean isExportFeatures()
235       {
236         return true;
237       }
238
239       @Override
240       public boolean isExportAnnotations()
241       {
242         return true;
243       }
244
245       @Override
246       public boolean isCancelled()
247       {
248         return false;
249       }
250
251       @Override
252       public AlignmentExportData getAlignExportData()
253       {
254         // TODO Auto-generated method stub
255         return null;
256       }
257
258       @Override
259       public void addActionListener(ActionListener actionListener)
260       {
261         // TODO Auto-generated method stub
262         
263       }
264
265       @Override
266       public void doShowSettings()
267       {
268         // TODO Auto-generated method stub
269         
270       }
271     };
272
273     AppletFormatAdapter formatAdapter = new AppletFormatAdapter();
274     try
275     {
276       alignment = (Alignment) formatAdapter.readFile(TEST_JSON_FILE,
277               DataSourceType.FILE, FileFormat.Json);
278       jf = (JSONFile) formatAdapter.getAlignFile();
279
280       AlignFrame af = new AlignFrame(alignment, jf.getHiddenSequences(),
281               jf.getHiddenColumns(), AlignFrame.DEFAULT_WIDTH,
282               AlignFrame.DEFAULT_HEIGHT);
283       af.getViewport().setShowSequenceFeatures(jf.isShowSeqFeatures());
284       String colourSchemeName = jf.getGlobalColourScheme();
285       ColourSchemeI cs = ColourSchemeMapper.getJalviewColourScheme(
286               colourSchemeName, alignment);
287       af.changeColour(cs);
288       af.getViewport().setFeaturesDisplayed(jf.getDisplayedFeatures());
289
290       formatAdapter = new AppletFormatAdapter(af.alignPanel, exportSettings);
291       String jsonOutput = formatAdapter.formatSequences(FileFormat.Json,
292               af.alignPanel.getAlignment(), false);
293
294       formatAdapter = new AppletFormatAdapter();
295       testAlignment = formatAdapter.readFile(jsonOutput,
296               DataSourceType.PASTE, FileFormat.Json);
297       testJsonFile = (JSONFile) formatAdapter.getAlignFile();
298       // System.out.println(jsonOutput);
299     } catch (IOException e)
300     {
301       e.printStackTrace();
302     }
303
304   }
305
306   @BeforeMethod(alwaysRun = true)
307   public void methodSetup()
308   {
309     passedCount = 0;
310   }
311
312   @AfterTest(alwaysRun = true)
313   public void tearDown() throws Exception
314   {
315     testJsonFile = null;
316     alignment = null;
317     expectedSeqs = null;
318     expectedAnnots = null;
319     expectedGrps = null;
320     testAlignment = null;
321     jf = null;
322   }
323
324   @Test(groups = { "Functional" })
325   public void roundTripTest()
326   {
327     assertNotNull("JSON roundtrip test failed!", testJsonFile);
328   }
329
330   @Test(groups = { "Functional" })
331   public void testSeqParsed()
332   {
333     assertNotNull("Couldn't read supplied alignment data.", testAlignment);
334     Assert.assertNotNull(testAlignment.getSequences());
335     for (SequenceI seq : testAlignment.getSequences())
336     {
337       SequenceI expectedSeq = expectedSeqs.get(seq.getName());
338       AssertJUnit.assertTrue(
339               "Failed Sequence Test  for >>> " + seq.getName(),
340               isSeqMatched(expectedSeq, seq));
341       passedCount++;
342     }
343     AssertJUnit.assertEquals("Some Sequences did not pass the test",
344             TEST_SEQ_HEIGHT, passedCount);
345   }
346
347   @Test(groups = { "Functional" })
348   public void hiddenColsTest()
349   {
350     HiddenColumns cs = testJsonFile.getHiddenColumns();
351     Assert.assertNotNull(cs);
352
353     Iterator<int[]> it = cs.iterator();
354     Iterator<int[]> colselit = expectedColSel.iterator();
355     Assert.assertTrue(it.hasNext());
356     Assert.assertEquals(cs.getNumberOfRegions(), TEST_CS_HEIGHT);
357     Assert.assertEquals(it.next(), colselit.next(),
358             "Mismatched hidden columns!");
359   }
360
361   @Test(groups = { "Functional" })
362   public void hiddenSeqsTest()
363   {
364     Assert.assertNotNull(testJsonFile.getHiddenSequences(),
365             "Hidden sequence Expected but found Null");
366     Assert.assertEquals(jf.getHiddenSequences().length, 1,
367             "Hidden sequence");
368   }
369
370   @Test(groups = { "Functional" })
371   public void colorSchemeTest()
372   {
373     Assert.assertNotNull(testJsonFile.getGlobalColourScheme(),
374             "Colourscheme is null, parsing failed!");
375     Assert.assertEquals(testJsonFile.getGlobalColourScheme(), "Zappo",
376             "Zappo colour scheme expected!");
377   }
378
379   /**
380    * Test for bug JAL-2489, NPE when exporting BioJSON with global colour
381    * scheme, and a group colour scheme, set as 'None'
382    */
383   @Test(groups = { "Functional" })
384   public void testBioJSONRoundTripWithColourSchemeNone()
385   {
386     AppletFormatAdapter formatAdapter = new AppletFormatAdapter();
387
388     Alignment _alignment;
389     try
390     {
391       // load example BioJSON file
392       _alignment = (Alignment) formatAdapter.readFile(TEST_JSON_FILE,
393               DataSourceType.FILE, FileFormat.Json);
394       JSONFile bioJsonFile = (JSONFile) formatAdapter.getAlignFile();
395       AlignFrame alignFrame = new AlignFrame(_alignment,
396               bioJsonFile.getHiddenSequences(),
397               bioJsonFile.getHiddenColumns(), AlignFrame.DEFAULT_WIDTH,
398               AlignFrame.DEFAULT_HEIGHT);
399
400       /*
401        * Create a group on the alignment;
402        * Change global and group colour scheme to 'None' and perform round trip
403        */
404       SequenceGroup sg = new SequenceGroup();
405       sg.addSequence(_alignment.getSequenceAt(0), false);
406       sg.setColourScheme(null);
407       ColourSchemeI cs = ColourSchemeMapper.getJalviewColourScheme(
408               ResidueColourScheme.NONE, _alignment);
409       alignFrame.changeColour(cs);
410       alignFrame.getViewport().setFeaturesDisplayed(
411               bioJsonFile.getDisplayedFeatures());
412       formatAdapter = new AppletFormatAdapter(alignFrame.alignPanel,
413               exportSettings);
414       // export BioJSON string
415       String jsonOutput = formatAdapter.formatSequences(FileFormat.Json,
416               alignFrame.alignPanel.getAlignment(), false);
417       // read back Alignment from BioJSON string
418       formatAdapter = new AppletFormatAdapter();
419       formatAdapter.readFile(jsonOutput, DataSourceType.PASTE,
420               FileFormat.Json);
421       // assert 'None' colour scheme is retained after round trip
422       JSONFile _bioJsonFile = (JSONFile) formatAdapter.getAlignFile();
423       Assert.assertEquals(_bioJsonFile.getGlobalColourScheme(),
424               ResidueColourScheme.NONE);
425     } catch (IOException e)
426     {
427       e.printStackTrace();
428     }
429   }
430
431   @Test(groups = { "Functional" })
432   public void isShowSeqFeaturesSet()
433   {
434     Assert.assertTrue(testJsonFile.isShowSeqFeatures(),
435             "Sequence feature isDisplayed setting expected to be true");
436   }
437
438   @Test(groups = { "Functional" })
439   public void testGrpParsed()
440   {
441     Assert.assertNotNull(testAlignment.getGroups());
442     for (SequenceGroup seqGrp : testAlignment.getGroups())
443     {
444       SequenceGroup expectedGrp = expectedGrps.get(seqGrp.getName());
445       AssertJUnit.assertTrue(
446               "Failed SequenceGroup Test for >>> " + seqGrp.getName(),
447               isGroupMatched(expectedGrp, seqGrp));
448       passedCount++;
449     }
450     AssertJUnit.assertEquals("Some SequenceGroups did not pass the test",
451             TEST_GRP_HEIGHT, passedCount);
452   }
453
454   @Test(groups = { "Functional" })
455   public void testAnnotationParsed()
456   {
457     Assert.assertNotNull(testAlignment.getAlignmentAnnotation());
458     for (AlignmentAnnotation annot : testAlignment.getAlignmentAnnotation())
459     {
460       AlignmentAnnotation expectedAnnot = expectedAnnots.get(annot.label);
461       AssertJUnit.assertTrue("Failed AlignmentAnnotation Test for >>> "
462               + annot.label, isAnnotationMatched(expectedAnnot, annot));
463       passedCount++;
464     }
465     AssertJUnit.assertEquals("Some Sequences did not pass the test",
466             TEST_ANOT_HEIGHT, passedCount);
467   }
468
469   public boolean isAnnotationMatched(AlignmentAnnotation eAnnot,
470           AlignmentAnnotation annot)
471   {
472     if (!eAnnot.label.equals(annot.label)
473             || !eAnnot.description.equals(annot.description)
474             || eAnnot.annotations.length != annot.annotations.length)
475     {
476       return false;
477     }
478
479     for (int x = 0; x < annot.annotations.length; x++)
480     {
481       Annotation y = annot.annotations[x];
482       Annotation z = annot.annotations[x];
483
484       if (!y.displayCharacter.equals(z.displayCharacter)
485               || y.value != z.value
486               || y.secondaryStructure != z.secondaryStructure)
487       {
488         return false;
489       }
490     }
491     return true;
492   }
493
494   boolean isSeqMatched(SequenceI expectedSeq, SequenceI actualSeq)
495   {
496     System.out.println("Testing >>> " + actualSeq.getName());
497
498     if (expectedSeq.getName().equals(actualSeq.getName())
499             && expectedSeq.getSequenceAsString().equals(
500                     actualSeq.getSequenceAsString())
501             && expectedSeq.getStart() == actualSeq.getStart()
502             && expectedSeq.getEnd() == actualSeq.getEnd()
503             && featuresMatched(expectedSeq, actualSeq))
504     {
505       return true;
506     }
507     return false;
508   }
509
510   public boolean isGroupMatched(SequenceGroup expectedGrp,
511           SequenceGroup actualGrp)
512   {
513
514     System.out.println("Testing >>> " + actualGrp.getName());
515     System.out.println(expectedGrp.getName() + " | " + actualGrp.getName());
516     System.out.println(expectedGrp.getColourText() + " | "
517             + actualGrp.getColourText());
518     System.out.println(expectedGrp.getDisplayBoxes() + " | "
519             + actualGrp.getDisplayBoxes());
520     System.out.println(expectedGrp.getIgnoreGapsConsensus() + " | "
521             + actualGrp.getIgnoreGapsConsensus());
522     System.out.println(expectedGrp.getSequences().size() + " | "
523             + actualGrp.getSequences().size());
524     System.out.println(expectedGrp.getStartRes() + " | "
525             + actualGrp.getStartRes());
526     System.out.println(expectedGrp.getEndRes() + " | "
527             + actualGrp.getEndRes());
528     System.out.println(expectedGrp.cs.getColourScheme() + " | "
529             + actualGrp.cs.getColourScheme());
530
531     boolean colourSchemeMatches = (expectedGrp.cs.getColourScheme() == null && actualGrp.cs
532             .getColourScheme() == null)
533             || expectedGrp.cs.getColourScheme().getClass()
534                     .equals(actualGrp.cs.getColourScheme().getClass());
535     if (expectedGrp.getName().equals(actualGrp.getName())
536             && expectedGrp.getColourText() == actualGrp.getColourText()
537             && expectedGrp.getDisplayBoxes() == actualGrp.getDisplayBoxes()
538             && expectedGrp.getIgnoreGapsConsensus() == actualGrp
539                     .getIgnoreGapsConsensus()
540             && colourSchemeMatches
541             && expectedGrp.getSequences().size() == actualGrp
542                     .getSequences().size()
543             && expectedGrp.getStartRes() == actualGrp.getStartRes()
544             && expectedGrp.getEndRes() == actualGrp.getEndRes())
545     {
546       return true;
547     }
548     return false;
549   }
550
551   private boolean featuresMatched(SequenceI seq1, SequenceI seq2)
552   {
553     try
554     {
555       if (seq1 == null && seq2 == null)
556       {
557         return true;
558       }
559
560       List<SequenceFeature> inFeature = seq1.getFeatures().getAllFeatures();
561       List<SequenceFeature> outFeature = seq2.getFeatures()
562               .getAllFeatures();
563
564       if (inFeature.size() != outFeature.size())
565       {
566         System.err.println("Feature count in: " + inFeature.size()
567                 + ", out: " + outFeature.size());
568         return false;
569       }
570
571       SequenceFeatures.sortFeatures(inFeature, true);
572       SequenceFeatures.sortFeatures(outFeature, true);
573       int i = 0;
574       for (SequenceFeature in : inFeature)
575       {
576         SequenceFeature out = outFeature.get(i);
577         /*
578         System.out.println(out.getType() + " | " + in.getType());
579         System.out.println(out.getBegin() + " | " + in.getBegin());
580         System.out.println(out.getEnd() + " | " + in.getEnd());
581         */
582         if (!in.equals(out))
583         {
584           System.err.println("Mismatch of " + in.toString() + " "
585                   + out.toString());
586           return false;
587         }
588         /*
589                 if (in.getBegin() == out.getBegin() && in.getEnd() == out.getEnd()
590                         && in.getScore() == out.getScore()
591                         && in.getFeatureGroup().equals(out.getFeatureGroup())
592                         && in.getType().equals(out.getType())
593                         && mapsMatch(in.otherDetails, out.otherDetails))
594                 {
595                 }
596                 else
597                 {
598                   System.err.println("Feature[" + i + "] mismatch, in: "
599                           + in.toString() + ", out: "
600                           + outFeature.get(i).toString());
601                   return false;
602                 }
603                 */
604         i++;
605       }
606     } catch (Exception e)
607     {
608       e.printStackTrace();
609     }
610     // System.out.println(">>>>>>>>>>>>>> features matched : " + matched);
611     return true;
612   }
613
614   boolean mapsMatch(Map<String, Object> m1, Map<String, Object> m2)
615   {
616     if (m1 == null || m2 == null)
617     {
618       if (m1 != null || m2 != null)
619       {
620         System.err
621                 .println("only one SequenceFeature.otherDetails is not null");
622         return false;
623       }
624       else
625       {
626         return true;
627       }
628     }
629     if (m1.size() != m2.size())
630     {
631       System.err.println("otherDetails map different sizes");
632       return false;
633     }
634     for (String key : m1.keySet())
635     {
636       if (!m2.containsKey(key))
637       {
638         System.err.println(key + " in only one otherDetails");
639         return false;
640       }
641       if (m1.get(key) == null && m2.get(key) != null || m1.get(key) != null
642               && m2.get(key) == null || !m1.get(key).equals(m2.get(key)))
643       {
644         System.err.println(key + " values in otherDetails don't match");
645         return false;
646       }
647     }
648     return true;
649   }
650
651   /**
652    * Test group roundtrip with null (None) group colour scheme
653    * 
654    * @throws IOException
655    */
656   @Test(groups = { "Functional" })
657   public void testGrpParsed_colourNone() throws IOException
658   {
659     AlignmentI copy = new Alignment(testAlignment);
660     SequenceGroup sg = testAlignment.getGroups().get(0);
661     SequenceGroup copySg = new SequenceGroup(new ArrayList<SequenceI>(),
662             sg.getName(),
663             null, sg.getDisplayBoxes(), sg.getDisplayText(),
664             sg.getColourText(), sg.getStartRes(), sg.getEndRes());
665     for (SequenceI seq : sg.getSequences())
666     {
667       int seqIndex = testAlignment.findIndex(seq);
668       copySg.addSequence(copy.getSequenceAt(seqIndex), false);
669     }
670     copy.addGroup(copySg);
671
672     AlignFrame af = new AlignFrame(copy, copy.getWidth(), copy.getHeight());
673     AppletFormatAdapter formatAdapter = new AppletFormatAdapter(
674             af.alignPanel);
675     String jsonOutput = formatAdapter.formatSequences(FileFormat.Json,
676             copy, false);
677     formatAdapter = new AppletFormatAdapter();
678     AlignmentI newAlignment = formatAdapter.readFile(jsonOutput,
679             DataSourceType.PASTE, FileFormat.Json);
680
681     Assert.assertNotNull(newAlignment.getGroups());
682     for (SequenceGroup seqGrp : newAlignment.getGroups())
683     {
684       SequenceGroup expectedGrp = copySg;
685       AssertJUnit.assertTrue(
686               "Failed SequenceGroup Test for >>> " + seqGrp.getName(),
687               isGroupMatched(expectedGrp, seqGrp));
688       passedCount++;
689     }
690     AssertJUnit.assertEquals("Some SequenceGroups did not pass the test",
691             TEST_GRP_HEIGHT, passedCount);
692   }
693 }