1 /* 2 * Jalview - A Sequence Alignment Editor and Viewer (2.11.1.4) 3 * Copyright (C) 2021 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.gui; 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.assertSame; 28 import static org.testng.AssertJUnit.assertTrue; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 33 import org.testng.Assert; 34 import org.testng.annotations.BeforeClass; 35 import org.testng.annotations.BeforeMethod; 36 import org.testng.annotations.Test; 37 38 import jalview.bin.Cache; 39 import jalview.bin.Jalview; 40 import jalview.datamodel.AlignedCodonFrame; 41 import jalview.datamodel.Alignment; 42 import jalview.datamodel.AlignmentAnnotation; 43 import jalview.datamodel.AlignmentI; 44 import jalview.datamodel.Annotation; 45 import jalview.datamodel.SearchResults; 46 import jalview.datamodel.SearchResultsI; 47 import jalview.datamodel.Sequence; 48 import jalview.datamodel.SequenceGroup; 49 import jalview.datamodel.SequenceI; 50 import jalview.io.DataSourceType; 51 import jalview.io.FileLoader; 52 import jalview.schemes.ClustalxColourScheme; 53 import jalview.schemes.ColourSchemeI; 54 import jalview.schemes.PIDColourScheme; 55 import jalview.structure.StructureSelectionManager; 56 import jalview.util.MapList; 57 import jalview.viewmodel.ViewportRanges; 58 59 public class AlignViewportTest 60 { 61 62 @BeforeClass(alwaysRun = true) setUpJvOptionPane()63 public void setUpJvOptionPane() 64 { 65 JvOptionPane.setInteractiveMode(false); 66 JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); 67 } 68 69 AlignmentI al; 70 71 AlignViewport testee; 72 73 @BeforeClass(alwaysRun = true) setUpBeforeClass()74 public static void setUpBeforeClass() throws Exception 75 { 76 Jalview.main(new String[] { "-nonews", "-props", 77 "test/jalview/testProps.jvprops" }); 78 79 /* 80 * remove any sequence mappings left lying around by other tests 81 */ 82 StructureSelectionManager ssm = StructureSelectionManager 83 .getStructureSelectionManager(Desktop.instance); 84 ssm.resetAll(); 85 } 86 87 @BeforeMethod(alwaysRun = true) setUp()88 public void setUp() 89 { 90 SequenceI seq1 = new Sequence("Seq1", "ABC"); 91 SequenceI seq2 = new Sequence("Seq2", "ABC"); 92 SequenceI seq3 = new Sequence("Seq3", "ABC"); 93 SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3 }; 94 al = new Alignment(seqs); 95 al.setDataset(null); 96 testee = new AlignViewport(al); 97 } 98 99 /** 100 * Test that a mapping is not deregistered when a second view is closed but 101 * the first still holds a reference to the mapping 102 */ 103 @Test(groups = { "Functional" }) testDeregisterMapping_onCloseView()104 public void testDeregisterMapping_onCloseView() 105 { 106 /* 107 * alignment with reference to mappings 108 */ 109 AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded( 110 ">Seq1\nCAGT\n", DataSourceType.PASTE); 111 112 SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0); 113 AlignedCodonFrame acf1 = new AlignedCodonFrame(); 114 acf1.addMap(s1, s1, new MapList(new int[] { 1, 4 }, new int[] { 1, 4 }, 115 1, 1)); 116 AlignedCodonFrame acf2 = new AlignedCodonFrame(); 117 acf2.addMap(s1, s1, new MapList(new int[] { 1, 4 }, new int[] { 4, 1 }, 118 1, 1)); 119 120 List<AlignedCodonFrame> mappings = new ArrayList<>(); 121 mappings.add(acf1); 122 mappings.add(acf2); 123 af1.getViewport().getAlignment().setCodonFrames(mappings); 124 af1.newView_actionPerformed(null); 125 126 /* 127 * Verify that creating the alignment for the new View has registered the 128 * mappings 129 */ 130 StructureSelectionManager ssm = StructureSelectionManager 131 .getStructureSelectionManager(Desktop.instance); 132 List<AlignedCodonFrame> sequenceMappings = ssm.getSequenceMappings(); 133 assertEquals(2, sequenceMappings.size()); 134 assertTrue(sequenceMappings.contains(acf1)); 135 assertTrue(sequenceMappings.contains(acf2)); 136 137 /* 138 * Close the second view. Verify that mappings are not removed as the first 139 * view still holds a reference to them. 140 */ 141 af1.closeMenuItem_actionPerformed(false); 142 assertEquals(2, sequenceMappings.size()); 143 assertTrue(sequenceMappings.contains(acf1)); 144 assertTrue(sequenceMappings.contains(acf2)); 145 } 146 147 /** 148 * Test that a mapping is deregistered if no alignment holds a reference to it 149 */ 150 @Test(groups = { "Functional" }) testDeregisterMapping_withNoReference()151 public void testDeregisterMapping_withNoReference() 152 { 153 Desktop d = Desktop.instance; 154 assertNotNull(d); 155 StructureSelectionManager ssm = StructureSelectionManager 156 .getStructureSelectionManager(Desktop.instance); 157 ssm.resetAll(); 158 159 AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded( 160 ">Seq1\nRSVQ\n", DataSourceType.PASTE); 161 AlignFrame af2 = new FileLoader().LoadFileWaitTillLoaded( 162 ">Seq2\nDGEL\n", DataSourceType.PASTE); 163 SequenceI cs1 = new Sequence("cseq1", "CCCGGGTTTAAA"); 164 SequenceI cs2 = new Sequence("cseq2", "CTTGAGTCTAGA"); 165 SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0); 166 SequenceI s2 = af2.getViewport().getAlignment().getSequenceAt(0); 167 // need to be distinct 168 AlignedCodonFrame acf1 = new AlignedCodonFrame(); 169 acf1.addMap(cs1, s1, new MapList(new int[] { 1, 4 }, 170 new int[] { 1, 12 }, 1, 3)); 171 AlignedCodonFrame acf2 = new AlignedCodonFrame(); 172 acf2.addMap(cs2, s2, new MapList(new int[] { 1, 4 }, 173 new int[] { 1, 12 }, 1, 3)); 174 AlignedCodonFrame acf3 = new AlignedCodonFrame(); 175 acf3.addMap(cs2, cs2, new MapList(new int[] { 1, 12 }, new int[] { 1, 176 12 }, 1, 1)); 177 178 List<AlignedCodonFrame> mappings1 = new ArrayList<>(); 179 mappings1.add(acf1); 180 af1.getViewport().getAlignment().setCodonFrames(mappings1); 181 182 List<AlignedCodonFrame> mappings2 = new ArrayList<>(); 183 mappings2.add(acf2); 184 mappings2.add(acf3); 185 af2.getViewport().getAlignment().setCodonFrames(mappings2); 186 187 /* 188 * AlignFrame1 has mapping acf1, AlignFrame2 has acf2 and acf3 189 */ 190 191 List<AlignedCodonFrame> ssmMappings = ssm.getSequenceMappings(); 192 assertEquals(0, ssmMappings.size()); 193 ssm.registerMapping(acf1); 194 assertEquals(1, ssmMappings.size()); 195 ssm.registerMapping(acf2); 196 assertEquals(2, ssmMappings.size()); 197 ssm.registerMapping(acf3); 198 assertEquals(3, ssmMappings.size()); 199 200 /* 201 * Closing AlignFrame2 should remove its mappings from 202 * StructureSelectionManager, since AlignFrame1 has no reference to them 203 */ 204 af2.closeMenuItem_actionPerformed(true); 205 assertEquals(1, ssmMappings.size()); 206 assertTrue(ssmMappings.contains(acf1)); 207 } 208 209 /** 210 * Test that a mapping is not deregistered if another alignment holds a 211 * reference to it 212 */ 213 @Test(groups = { "Functional" }) testDeregisterMapping_withReference()214 public void testDeregisterMapping_withReference() 215 { 216 Desktop d = Desktop.instance; 217 assertNotNull(d); 218 StructureSelectionManager ssm = StructureSelectionManager 219 .getStructureSelectionManager(Desktop.instance); 220 ssm.resetAll(); 221 222 AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded( 223 ">Seq1\nRSVQ\n", DataSourceType.PASTE); 224 AlignFrame af2 = new FileLoader().LoadFileWaitTillLoaded( 225 ">Seq2\nDGEL\n", DataSourceType.PASTE); 226 SequenceI cs1 = new Sequence("cseq1", "CCCGGGTTTAAA"); 227 SequenceI cs2 = new Sequence("cseq2", "CTTGAGTCTAGA"); 228 SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0); 229 SequenceI s2 = af2.getViewport().getAlignment().getSequenceAt(0); 230 // need to be distinct 231 AlignedCodonFrame acf1 = new AlignedCodonFrame(); 232 acf1.addMap(cs1, s1, new MapList(new int[] { 1, 4 }, 233 new int[] { 1, 12 }, 1, 3)); 234 AlignedCodonFrame acf2 = new AlignedCodonFrame(); 235 acf2.addMap(cs2, s2, new MapList(new int[] { 1, 4 }, 236 new int[] { 1, 12 }, 1, 3)); 237 AlignedCodonFrame acf3 = new AlignedCodonFrame(); 238 acf3.addMap(cs2, cs2, new MapList(new int[] { 1, 12 }, new int[] { 1, 239 12 }, 1, 1)); 240 241 List<AlignedCodonFrame> mappings1 = new ArrayList<>(); 242 mappings1.add(acf1); 243 mappings1.add(acf2); 244 af1.getViewport().getAlignment().setCodonFrames(mappings1); 245 246 List<AlignedCodonFrame> mappings2 = new ArrayList<>(); 247 mappings2.add(acf2); 248 mappings2.add(acf3); 249 af2.getViewport().getAlignment().setCodonFrames(mappings2); 250 251 /* 252 * AlignFrame1 has mappings acf1 and acf2, AlignFrame2 has acf2 and acf3 253 */ 254 255 List<AlignedCodonFrame> ssmMappings = ssm.getSequenceMappings(); 256 assertEquals(0, ssmMappings.size()); 257 ssm.registerMapping(acf1); 258 assertEquals(1, ssmMappings.size()); 259 ssm.registerMapping(acf2); 260 assertEquals(2, ssmMappings.size()); 261 ssm.registerMapping(acf3); 262 assertEquals(3, ssmMappings.size()); 263 264 /* 265 * Closing AlignFrame2 should remove mapping acf3 from 266 * StructureSelectionManager, but not acf2, since AlignFrame1 still has a 267 * reference to it 268 */ 269 af2.closeMenuItem_actionPerformed(true); 270 assertEquals(2, ssmMappings.size()); 271 assertTrue(ssmMappings.contains(acf1)); 272 assertTrue(ssmMappings.contains(acf2)); 273 assertFalse(ssmMappings.contains(acf3)); 274 } 275 276 /** 277 * Test for JAL-1306 - conservation thread should run even when only Quality 278 * (and not Conservation) is enabled in Preferences 279 */ 280 @Test(groups = { "Functional" }, timeOut=2000) testUpdateConservation_qualityOnly()281 public void testUpdateConservation_qualityOnly() 282 { 283 Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", 284 Boolean.TRUE.toString()); 285 Cache.applicationProperties.setProperty("SHOW_QUALITY", 286 Boolean.TRUE.toString()); 287 Cache.applicationProperties.setProperty("SHOW_CONSERVATION", 288 Boolean.FALSE.toString()); 289 Cache.applicationProperties.setProperty("SHOW_OCCUPANCY", 290 Boolean.FALSE.toString()); 291 Cache.applicationProperties.setProperty("SHOW_IDENTITY", 292 Boolean.FALSE.toString()); 293 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded( 294 "examples/uniref50.fa", DataSourceType.FILE); 295 296 /* 297 * wait for Conservation thread to complete 298 */ 299 AlignViewport viewport = af.getViewport(); 300 synchronized (this) 301 { 302 while (viewport.getCalcManager().isWorking()) 303 { 304 try 305 { 306 wait(50); 307 } catch (InterruptedException e) 308 { 309 } 310 } 311 } 312 AlignmentAnnotation[] anns = viewport.getAlignment() 313 .getAlignmentAnnotation(); 314 assertNotNull("No annotations found", anns); 315 assertEquals("More than one annotation found", 1, anns.length); 316 assertTrue("Annotation is not Quality", 317 anns[0].description.startsWith("Alignment Quality")); 318 Annotation[] annotations = anns[0].annotations; 319 assertNotNull("Quality annotations are null", annotations); 320 assertNotNull("Quality in column 1 is null", annotations[0]); 321 assertTrue("No quality value in column 1", annotations[0].value > 10f); 322 } 323 324 @Test(groups = { "Functional" }) testSetGlobalColourScheme()325 public void testSetGlobalColourScheme() 326 { 327 /* 328 * test for JAL-2283: don't inadvertently turn on colour by conservation 329 */ 330 Cache.applicationProperties.setProperty("DEFAULT_COLOUR_PROT", "None"); 331 Cache.applicationProperties.setProperty("SHOW_CONSERVATION", 332 Boolean.TRUE.toString()); 333 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded( 334 "examples/uniref50.fa", DataSourceType.FILE); 335 ColourSchemeI cs = new PIDColourScheme(); 336 AlignViewport viewport = af.getViewport(); 337 viewport.setGlobalColourScheme(cs); 338 assertFalse(viewport.getResidueShading() 339 .conservationApplied()); 340 341 /* 342 * JAL-3201 groups have their own ColourSchemeI instances 343 */ 344 AlignmentI aln = viewport.getAlignment(); 345 SequenceGroup sg1 = new SequenceGroup(); 346 sg1.addSequence(aln.getSequenceAt(0), false); 347 sg1.addSequence(aln.getSequenceAt(2), false); 348 SequenceGroup sg2 = new SequenceGroup(); 349 sg2.addSequence(aln.getSequenceAt(1), false); 350 sg2.addSequence(aln.getSequenceAt(3), false); 351 aln.addGroup(sg1); 352 aln.addGroup(sg2); 353 viewport.setColourAppliesToAllGroups(true); 354 viewport.setGlobalColourScheme(new ClustalxColourScheme()); 355 ColourSchemeI cs0 = viewport.getGlobalColourScheme(); 356 ColourSchemeI cs1 = sg1.getColourScheme(); 357 ColourSchemeI cs2 = sg2.getColourScheme(); 358 assertTrue(cs0 instanceof ClustalxColourScheme); 359 assertTrue(cs1 instanceof ClustalxColourScheme); 360 assertTrue(cs2 instanceof ClustalxColourScheme); 361 assertNotSame(cs0, cs1); 362 assertNotSame(cs0, cs2); 363 assertNotSame(cs1, cs2); 364 } 365 366 @Test(groups = { "Functional" }) testSetGetHasSearchResults()367 public void testSetGetHasSearchResults() 368 { 369 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded( 370 "examples/uniref50.fa", DataSourceType.FILE); 371 SearchResultsI sr = new SearchResults(); 372 SequenceI s1 = af.getViewport().getAlignment().getSequenceAt(0); 373 374 // create arbitrary range on first sequence 375 sr.addResult(s1, s1.getStart() + 10, s1.getStart() + 15); 376 377 // test set 378 af.getViewport().setSearchResults(sr); 379 // has -> true 380 assertTrue(af.getViewport().hasSearchResults()); 381 // get == original 382 assertEquals(sr, af.getViewport().getSearchResults()); 383 384 // set(null) results in has -> false 385 386 af.getViewport().setSearchResults(null); 387 assertFalse(af.getViewport().hasSearchResults()); 388 } 389 390 /** 391 * Verify that setting the selection group has the side-effect of setting the 392 * context on the group, unless it already has one, but does not change 393 * whether the group is defined or not. 394 */ 395 @Test(groups = { "Functional" }) testSetSelectionGroup()396 public void testSetSelectionGroup() 397 { 398 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded( 399 "examples/uniref50.fa", DataSourceType.FILE); 400 AlignViewport av = af.getViewport(); 401 SequenceGroup sg1 = new SequenceGroup(); 402 SequenceGroup sg2 = new SequenceGroup(); 403 SequenceGroup sg3 = new SequenceGroup(); 404 405 av.setSelectionGroup(sg1); 406 assertSame(sg1.getContext(), av.getAlignment()); // context set 407 assertFalse(sg1.isDefined()); // group not defined 408 409 sg2.setContext(sg1, false); 410 av.setSelectionGroup(sg2); 411 assertFalse(sg2.isDefined()); // unchanged 412 assertSame(sg2.getContext(), sg1); // unchanged 413 414 // create a defined group 415 sg3.setContext(av.getAlignment(), true); 416 av.setSelectionGroup(sg3); 417 assertTrue(sg3.isDefined()); // unchanged 418 } 419 /** 420 * Verify that setting/clearing SHOW_OCCUPANCY preference adds or omits occupancy row from viewport 421 */ 422 @Test(groups = { "Functional" }) testShowOrDontShowOccupancy()423 public void testShowOrDontShowOccupancy() 424 { 425 // disable occupancy 426 jalview.bin.Cache.setProperty("SHOW_OCCUPANCY", Boolean.FALSE.toString()); 427 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded( 428 "examples/uniref50.fa", DataSourceType.FILE); 429 AlignViewport av = af.getViewport(); 430 Assert.assertNull(av.getAlignmentGapAnnotation(), "Preference did not disable occupancy row."); 431 int c = 0; 432 for (AlignmentAnnotation aa : av.getAlignment().findAnnotations(null, 433 null, "Occupancy")) 434 { 435 c++; 436 } 437 Assert.assertEquals(c, 0, "Expected zero occupancy rows."); 438 439 // enable occupancy 440 jalview.bin.Cache.setProperty("SHOW_OCCUPANCY", Boolean.TRUE.toString()); 441 af = new FileLoader().LoadFileWaitTillLoaded( 442 "examples/uniref50.fa", DataSourceType.FILE); 443 av = af.getViewport(); 444 Assert.assertNotNull(av.getAlignmentGapAnnotation(), "Preference did not enable occupancy row."); 445 c = 0; 446 for (AlignmentAnnotation aa : av.getAlignment().findAnnotations(null, 447 null, av.getAlignmentGapAnnotation().label)) 448 { 449 c++; 450 } 451 ; 452 Assert.assertEquals(c, 1, "Expected to find one occupancy row."); 453 } 454 455 @Test(groups = { "Functional" }) testGetConsensusSeq()456 public void testGetConsensusSeq() 457 { 458 /* 459 * A-C 460 * A-C 461 * A-D 462 * --D 463 * consensus expected to be A-C 464 */ 465 String fasta = ">s1\nA-C\n>s2\nA-C\n>s3\nA-D\n>s4\n--D\n"; 466 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(fasta, 467 DataSourceType.PASTE); 468 AlignViewport testme = af.getViewport(); 469 SequenceI cons = testme.getConsensusSeq(); 470 assertEquals("A-C", cons.getSequenceAsString()); 471 } 472 473 @Test(groups = { "Functional" }) testHideRevealSequences()474 public void testHideRevealSequences() 475 { 476 ViewportRanges ranges = testee.getRanges(); 477 assertEquals(3, al.getHeight()); 478 assertEquals(0, ranges.getStartSeq()); 479 assertEquals(2, ranges.getEndSeq()); 480 481 /* 482 * hide first sequence 483 */ 484 testee.hideSequence(new SequenceI[] { al.getSequenceAt(0) }); 485 assertEquals(2, al.getHeight()); 486 assertEquals(0, ranges.getStartSeq()); 487 assertEquals(1, ranges.getEndSeq()); 488 489 /* 490 * reveal hidden sequences above the first 491 */ 492 testee.showSequence(0); 493 assertEquals(3, al.getHeight()); 494 assertEquals(0, ranges.getStartSeq()); 495 assertEquals(2, ranges.getEndSeq()); 496 497 /* 498 * hide first and third sequences 499 */ 500 testee.hideSequence(new SequenceI[] { al.getSequenceAt(0), 501 al.getSequenceAt(2) }); 502 assertEquals(1, al.getHeight()); 503 assertEquals(0, ranges.getStartSeq()); 504 assertEquals(0, ranges.getEndSeq()); 505 506 /* 507 * reveal all hidden sequences 508 */ 509 testee.showAllHiddenSeqs(); 510 assertEquals(3, al.getHeight()); 511 assertEquals(0, ranges.getStartSeq()); 512 assertEquals(2, ranges.getEndSeq()); 513 } 514 } 515