X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=test%2Fjalview%2Fio%2FStockholmFileTest.java;h=a6ae6307b005e0a16ee18943d9364771c95ec152;hb=bab4b08733a9543bdce5c0b9d1c32ca7d18ad9e3;hp=f6b39af3fe0f3ed13cbee47e2a5b7a1374fcd60e;hpb=0cc7012545b54289025e8c6f109b4f26dc61dae9;p=jalview.git diff --git a/test/jalview/io/StockholmFileTest.java b/test/jalview/io/StockholmFileTest.java index f6b39af..a6ae630 100644 --- a/test/jalview/io/StockholmFileTest.java +++ b/test/jalview/io/StockholmFileTest.java @@ -33,8 +33,10 @@ import jalview.datamodel.SequenceI; import jalview.gui.JvOptionPane; import java.io.File; +import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.testng.Assert; @@ -52,13 +54,14 @@ public class StockholmFileTest } static String PfamFile = "examples/PF00111_seed.stk", - RfamFile = "examples/RF00031_folded.stk"; + RfamFile = "examples/RF00031_folded.stk", + RnaSSTestFile = "examples/rna_ss_test.stk"; @Test(groups = { "Functional" }) public void pfamFileIO() throws Exception { testFileIOwithFormat(new File(PfamFile), FileFormat.Stockholm, -1, 0, - false); + false, false, false); } @Test(groups = { "Functional" }) @@ -84,7 +87,7 @@ public class StockholmFileTest public void rfamFileIO() throws Exception { testFileIOwithFormat(new File(RfamFile), FileFormat.Stockholm, 2, 1, - false); + false, false, false); } /** @@ -96,11 +99,14 @@ public class StockholmFileTest * @param ioformat * - label for IO class used to write and read back in the data from * f + * @param ignoreFeatures * @param ignoreRowVisibility + * @param allowNullAnnotations */ public static void testFileIOwithFormat(File f, FileFormatI ioformat, - int naliannot, int nminseqann, boolean ignoreRowVisibility) + int naliannot, int nminseqann, boolean ignoreFeatures, + boolean ignoreRowVisibility, boolean allowNullAnnotations) { System.out.println("Reading file: " + f); String ff = f.getPath(); @@ -134,7 +140,8 @@ public class StockholmFileTest "Identify routine could not recognise output generated by '" + ioformat + "' writer", ioformat.equals(identifyoutput)); - testAlignmentEquivalence(al, al_input, false); + testAlignmentEquivalence(al, al_input, ignoreFeatures, + ignoreRowVisibility, allowNullAnnotations); int numaliannot = 0, numsqswithali = 0; for (AlignmentAnnotation ala : al_input.getAlignmentAnnotation()) { @@ -178,10 +185,12 @@ public class StockholmFileTest public static void testAlignmentEquivalence(AlignmentI al, AlignmentI al_input, boolean ignoreFeatures) { + testAlignmentEquivalence(al, al_input, ignoreFeatures, false, false); } /** - * assert alignment equivalence + * assert alignment equivalence - uses special comparators for RNA structure + * annotation rows. * * @param al * 'original' @@ -194,10 +203,15 @@ public class StockholmFileTest * @param ignoreRowVisibility * when true, do not fail if there are differences in the visibility * of annotation rows + * @param allowNullAnnotation + * when true, positions in alignment annotation that are null will be + * considered equal to positions containing annotation where + * Annotation.isWhitespace() returns true. + * */ public static void testAlignmentEquivalence(AlignmentI al, AlignmentI al_input, boolean ignoreFeatures, - boolean ignoreRowVisibility) + boolean ignoreRowVisibility, boolean allowNullAnnotation) { assertNotNull("Original alignment was null", al); assertNotNull("Generated alignment was null", al_input); @@ -217,8 +231,8 @@ public class StockholmFileTest // we might want to revise this in future int aa_new_size = (aa_new == null ? 0 : aa_new.length); int aa_original_size = (aa_original == null ? 0 : aa_original.length); - Map orig_groups = new HashMap(); - Map new_groups = new HashMap(); + Map orig_groups = new HashMap<>(); + Map new_groups = new HashMap<>(); if (aa_new != null && aa_original != null) { @@ -228,7 +242,7 @@ public class StockholmFileTest { assertEqualSecondaryStructure( "Different alignment annotation at position " + i, - aa_original[i], aa_new[i]); + aa_original[i], aa_new[i], allowNullAnnotation); // compare graphGroup or graph properties - needed to verify JAL-1299 assertEquals("Graph type not identical.", aa_original[i].graph, aa_new[i].graph); @@ -274,7 +288,8 @@ public class StockholmFileTest seq_original = al.getSequencesArray(); SequenceI[] seq_new = new SequenceI[al_input.getSequencesArray().length]; seq_new = al_input.getSequencesArray(); - SequenceFeature[] sequenceFeatures_original, sequenceFeatures_new; + List sequenceFeatures_original; + List sequenceFeatures_new; AlignmentAnnotation annot_original, annot_new; // for (int i = 0; i < al.getSequencesArray().length; i++) @@ -310,23 +325,20 @@ public class StockholmFileTest && seq_new[in].getSequenceFeatures() != null) { System.out.println("There are feature!!!"); - sequenceFeatures_original = new SequenceFeature[seq_original[i] - .getSequenceFeatures().length]; sequenceFeatures_original = seq_original[i] .getSequenceFeatures(); - sequenceFeatures_new = new SequenceFeature[seq_new[in] - .getSequenceFeatures().length]; sequenceFeatures_new = seq_new[in].getSequenceFeatures(); - assertEquals("different number of features", - seq_original[i].getSequenceFeatures().length, - seq_new[in].getSequenceFeatures().length); + assertEquals("different number of features", seq_original[i] + .getSequenceFeatures().size(), seq_new[in] + .getSequenceFeatures().size()); - for (int feat = 0; feat < seq_original[i].getSequenceFeatures().length; feat++) + for (int feat = 0; feat < seq_original[i].getSequenceFeatures() + .size(); feat++) { assertEquals("Different features", - sequenceFeatures_original[feat], - sequenceFeatures_new[feat]); + sequenceFeatures_original.get(feat), + sequenceFeatures_new.get(feat)); } } // compare alignment annotation @@ -342,7 +354,7 @@ public class StockholmFileTest annot_new = al_input.getSequenceAt(in).getAnnotation()[j]; assertEqualSecondaryStructure( "Different annotation elements", annot_original, - annot_new); + annot_new, allowNullAnnotation); } } } @@ -364,9 +376,26 @@ public class StockholmFileTest } } + /** + * compare two annotation rows, with special support for secondary structure + * comparison. With RNA, only the value and the secondaryStructure symbols are + * compared, displayCharacter and description are ignored. Annotations where + * Annotation.isWhitespace() is true are always considered equal. + * + * @param message + * - not actually used yet.. + * @param annot_or + * - the original annotation + * @param annot_new + * - the one compared to the original annotation + * @param allowNullEquivalence + * when true, positions in alignment annotation that are null will be + * considered equal to non-null positions for which + * Annotation.isWhitespace() is true. + */ private static void assertEqualSecondaryStructure(String message, - AlignmentAnnotation annot_or, - AlignmentAnnotation annot_new) + AlignmentAnnotation annot_or, AlignmentAnnotation annot_new, + boolean allowNullEqivalence) { // TODO: test to cover this assert behaves correctly for all allowed // variations of secondary structure annotation row equivalence @@ -389,7 +418,8 @@ public class StockholmFileTest if (isRna) { if (an_or.secondaryStructure != an_new.secondaryStructure - || an_or.value != an_new.value) + || ((Float.isNaN(an_or.value) != Float + .isNaN(an_new.value)) || an_or.value != an_new.value)) { fail("Different RNA secondary structure at column " + i + " expected: [" + annot_or.annotations[i].toString() @@ -400,7 +430,8 @@ public class StockholmFileTest else { // not RNA secondary structure, so expect all elements to match... - if (!an_or.displayCharacter.trim().equals( + if ((an_or.isWhitespace() != an_new.isWhitespace()) + || !an_or.displayCharacter.trim().equals( an_new.displayCharacter.trim()) || !("" + an_or.secondaryStructure).trim().equals( ("" + an_new.secondaryStructure).trim()) @@ -408,7 +439,9 @@ public class StockholmFileTest .trim().length() == 0) || (an_new.description == null && an_or.description .trim().length() == 0) || an_or.description - .trim().equals(an_new.description.trim())))) + .trim().equals(an_new.description.trim()))) + || !((Float.isNaN(an_or.value) && Float + .isNaN(an_new.value)) || an_or.value == an_new.value)) { fail("Annotation Element Mismatch\nElement " + i + " in original: " + annot_or.annotations[i].toString() @@ -424,18 +457,133 @@ public class StockholmFileTest } else { - fail("Annotation Element Mismatch\nElement " - + i - + " in original: " - + (annot_or.annotations[i] == null ? "is null" - : annot_or.annotations[i].toString()) - + "\nElement " - + i - + " in new: " - + (annot_new.annotations[i] == null ? "is null" - : annot_new.annotations[i].toString())); + if (allowNullEqivalence) + { + if (an_or != null && an_or.isWhitespace()) + + { + continue; + } + if (an_new != null && an_new.isWhitespace()) + { + continue; + } + } + // need also to test for null in one, non-SS annotation in other... + fail("Annotation Element Mismatch\nElement " + i + " in original: " + + (an_or == null ? "is null" : an_or.toString()) + + "\nElement " + i + " in new: " + + (an_new == null ? "is null" : an_new.toString())); + } + } + } + + /** + * @see assertEqualSecondaryStructure - test if two secondary structure + * annotations are not equal + * @param message + * @param an_orig + * @param an_new + * @param allowNullEquivalence + */ + public static void assertNotEqualSecondaryStructure(String message, + AlignmentAnnotation an_orig, AlignmentAnnotation an_new, + boolean allowNullEquivalence) + { + boolean thrown = false; + try + { + assertEqualSecondaryStructure("", an_orig, an_new, + allowNullEquivalence); + } catch (AssertionError af) + { + thrown = true; + } + if (!thrown) + { + fail("Expected difference for [" + an_orig + "] and [" + an_new + "]"); + } + } + private AlignmentAnnotation makeAnnot(Annotation ae) + { + return new AlignmentAnnotation("label", "description", new Annotation[] + { ae }); + } + + @Test(groups={"Functional"}) + public void testAnnotationEquivalence() + { + AlignmentAnnotation one = makeAnnot(new Annotation("", "", ' ', 1)); + AlignmentAnnotation anotherOne = makeAnnot(new Annotation("", "", ' ', + 1)); + AlignmentAnnotation sheet = makeAnnot(new Annotation("","",'E',0f)); + AlignmentAnnotation anotherSheet = makeAnnot(new Annotation("","",'E',0f)); + AlignmentAnnotation sheetWithLabel = makeAnnot(new Annotation("1", "", + 'E', 0f)); + AlignmentAnnotation anotherSheetWithLabel = makeAnnot(new Annotation( + "1", "", 'E', 0f)); + AlignmentAnnotation rnaNoDC = makeAnnot(new Annotation("","",'<',0f)); + AlignmentAnnotation anotherRnaNoDC = makeAnnot(new Annotation("","",'<',0f)); + AlignmentAnnotation rnaWithDC = makeAnnot(new Annotation("B", "", '<', + 0f)); + AlignmentAnnotation anotherRnaWithDC = makeAnnot(new Annotation("B", + "", '<', 0f)); + + // check self equivalence + for (boolean allowNull : new boolean[] { true, false }) + { + assertEqualSecondaryStructure("Should be equal", one, anotherOne, + allowNull); + assertEqualSecondaryStructure("Should be equal", sheet, anotherSheet, + allowNull); + assertEqualSecondaryStructure("Should be equal", sheetWithLabel, + anotherSheetWithLabel, allowNull); + assertEqualSecondaryStructure("Should be equal", rnaNoDC, + anotherRnaNoDC, allowNull); + assertEqualSecondaryStructure("Should be equal", rnaWithDC, + anotherRnaWithDC, allowNull); + // display character doesn't matter for RNA structure (for 2.10.2) + assertEqualSecondaryStructure("Should be equal", rnaWithDC, rnaNoDC, + allowNull); + assertEqualSecondaryStructure("Should be equal", rnaNoDC, rnaWithDC, + allowNull); + } + + // verify others are different + List aaSet = Arrays.asList(one, sheet, + sheetWithLabel, rnaWithDC); + for (int p = 0; p < aaSet.size(); p++) + { + for (int q = 0; q < aaSet.size(); q++) + { + if (p != q) + { + assertNotEqualSecondaryStructure("Should be different", + aaSet.get(p), aaSet.get(q), false); + } + else + { + assertEqualSecondaryStructure("Should be same", aaSet.get(p), + aaSet.get(q), false); + assertEqualSecondaryStructure("Should be same", aaSet.get(p), + aaSet.get(q), true); + assertNotEqualSecondaryStructure( + "Should be different to empty anot", aaSet.get(p), + makeAnnot(Annotation.EMPTY_ANNOTATION), false); + assertNotEqualSecondaryStructure( + "Should be different to empty annot", + makeAnnot(Annotation.EMPTY_ANNOTATION), aaSet.get(q), + true); + assertNotEqualSecondaryStructure("Should be different to null", + aaSet.get(p), makeAnnot(null), false); + assertNotEqualSecondaryStructure("Should be different to null", + makeAnnot(null), aaSet.get(q), true); + } } } + + // test null + } String aliFile = ">Dm\nAAACCCUUUUACACACGGGAAAGGG"; @@ -444,9 +592,13 @@ public class StockholmFileTest + "(|(|(|(|, .|, .|, .|, .|)|)|)|)|\t0.0\nROWPROPERTIES\t" + "secondary structure\tscaletofit=true\tshowalllabs=true\tcentrelabs=false"; + String annFileCurlyWuss = "JALVIEW_ANNOTATION\n# Created: Thu May 04 11:16:52 BST 2017\n\n" + + "SEQUENCE_REF\tDm\nNO_GRAPH\tsecondary structure\tsecondary structure\t" + + "(|(|(|(||{|{||{|{||)|)|)|)||}|}|}|}|\t0.0\nROWPROPERTIES\t" + + "secondary structure\tscaletofit=true\tshowalllabs=true\tcentrelabs=false"; String annFileFullWuss = "JALVIEW_ANNOTATION\n# Created: Thu May 04 11:16:52 BST 2017\n\n" + "SEQUENCE_REF\tDm\nNO_GRAPH\tsecondary structure\tsecondary structure\t" - + "(|(|(|(||{|{||[|[||)|)|)|)||}|}|]|]\t0.0\nROWPROPERTIES\t" + + "(|(|(|(||{|{||[|[||)|)|)|)||}|}|]|]|\t0.0\nROWPROPERTIES\t" + "secondary structure\tscaletofit=true\tshowalllabs=true\tcentrelabs=false"; @Test(groups = { "Functional" }) @@ -456,6 +608,12 @@ public class StockholmFileTest } @Test(groups = { "Functional" }) + public void curlyWUSSsecondaryStructureForRNASequence() throws Exception + { + roundTripSSForRNA(aliFile, annFileCurlyWuss); + } + + @Test(groups = { "Functional" }) public void fullWUSSsecondaryStructureForRNASequence() throws Exception { roundTripSSForRNA(aliFile, annFileFullWuss); @@ -466,13 +624,13 @@ public class StockholmFileTest { for (char ch : new char[] { '{', '}', '[', ']', '(', ')', '<', '>' }) { - Assert.assertTrue(StockholmFile.DETECT_BRACKETS.matchAt("" + ch, 0), - "Didn't recognise " + ch + " as a WUSS bracket"); + Assert.assertTrue(StockholmFile.RNASS_BRACKETS.indexOf(ch) >= 0, + "Didn't recognise '" + ch + "' as a WUSS bracket"); } - for (char ch : new char[] { '@', '!', 'V', 'Q', '*', ' ', '-', '.' }) + for (char ch : new char[] { '@', '!', '*', ' ', '-', '.' }) { - Assert.assertFalse(StockholmFile.DETECT_BRACKETS.matchAt("" + ch, 0), - "Shouldn't recognise " + ch + " as a WUSS bracket"); + Assert.assertFalse(StockholmFile.RNASS_BRACKETS.indexOf(ch) >= 0, + "Shouldn't recognise '" + ch + "' as a WUSS bracket"); } } private static void roundTripSSForRNA(String aliFile, String annFile) @@ -494,7 +652,82 @@ public class StockholmFileTest // AlignmentUtils.showOrHideSequenceAnnotations(newAl.getViewport() // .getAlignment(), Arrays.asList("Secondary Structure"), newAl // .getViewport().getAlignment().getSequences(), true, true); - testAlignmentEquivalence(al, newAl, true, true); + testAlignmentEquivalence(al, newAl, true, true, true); } + + // this is the single sequence alignment and the SS annotations equivalent to + // the ones in file RnaSSTestFile + String aliFileRnaSSAlphaChars = ">Test.sequence/1-14\n" + + "GUACAAAAAAAAAA"; + String annFileRnaSSAlphaChars = "JALVIEW_ANNOTATION\n" + + "# Created: Thu Aug 02 14:54:57 BST 2018\n" + "\n" + + "NO_GRAPH\tSecondary Structure\tSecondary Structure\t<,<|(,(|E,E|H,H|B,B|h,h|e,e|b,b|(,(|E,E|),)|e,e|),)|>,>|\t2.0\n" + + "\n" + + "ROWPROPERTIES\tSecondary Structure\tscaletofit=true\tshowalllabs=true\tcentrelabs=false\n" + + "\n" + "\n" + "ALIGNMENT\tID=RNA.SS.TEST\tTP=RNA;"; + String wrongAnnFileRnaSSAlphaChars = "JALVIEW_ANNOTATION\n" + + "# Created: Thu Aug 02 14:54:57 BST 2018\n" + "\n" + + "NO_GRAPH\tSecondary Structure\tSecondary Structure\t<,<|(,(|H,H|E,E|B,B|h,h|e,e|b,b|(,(|E,E|),)|e,e|),)|>,>|\t2.0\n" + + "\n" + + "ROWPROPERTIES\tSecondary Structure\tscaletofit=true\tshowalllabs=true\tcentrelabs=false\n" + + "\n" + "\n" + "ALIGNMENT\tID=RNA.SS.TEST\tTP=RNA;"; + @Test(groups = { "Functional" }) + public void stockholmFileRnaSSAlphaChars() throws Exception + { + AppletFormatAdapter af = new AppletFormatAdapter(); + AlignmentI al = af.readFile(RnaSSTestFile, DataSourceType.FILE, + jalview.io.FileFormat.Stockholm); + Iterable aai = al.findAnnotations(null, null, + "Secondary Structure"); + AlignmentAnnotation aa = aai.iterator().next(); + Assert.assertTrue(aa.isRNA(), + "'" + RnaSSTestFile + "' not recognised as RNA SS"); + Assert.assertTrue(aa.isValidStruc(), + "'" + RnaSSTestFile + "' not recognised as valid structure"); + Annotation[] as = aa.annotations; + char[] As = new char[as.length]; + for (int i = 0; i < as.length; i++) + { + As[i] = as[i].secondaryStructure; + } + char[] shouldBe = { '<', '(', 'E', 'H', 'B', 'h', 'e', 'b', '(', 'E', + ')', 'e', ')', '>' }; + Assert.assertTrue( + Arrays.equals(As, shouldBe), + "Annotation is " + new String(As) + " but should be " + + new String(shouldBe)); + + // this should result in the same RNA SS Annotations + AlignmentI newAl = new AppletFormatAdapter().readFile( + aliFileRnaSSAlphaChars, + DataSourceType.PASTE, jalview.io.FileFormat.Fasta); + AnnotationFile aaf = new AnnotationFile(); + aaf.readAnnotationFile(newAl, annFileRnaSSAlphaChars, + DataSourceType.PASTE); + + Assert.assertTrue( + testRnaSSAnnotationsEquivalent(al.getAlignmentAnnotation()[0], + newAl.getAlignmentAnnotation()[0])); + + // this should NOT result in the same RNA SS Annotations + newAl = new AppletFormatAdapter().readFile( + aliFileRnaSSAlphaChars, DataSourceType.PASTE, + jalview.io.FileFormat.Fasta); + aaf = new AnnotationFile(); + aaf.readAnnotationFile(newAl, wrongAnnFileRnaSSAlphaChars, + DataSourceType.PASTE); + + boolean mismatch = testRnaSSAnnotationsEquivalent(al.getAlignmentAnnotation()[0], + newAl.getAlignmentAnnotation()[0]); + Assert.assertFalse( mismatch ); + } + + private static boolean testRnaSSAnnotationsEquivalent( + AlignmentAnnotation a1, + AlignmentAnnotation a2) + { + return a1.rnaSecondaryStructureEquivalent(a2); + } + }