Merge branch 'bug/JAL-3120restoreFeatureColour' into merge/JAL-3120
[jalview.git] / test / jalview / gui / FreeUpMemoryTest.java
index 2a9a8c9..9b21274 100644 (file)
@@ -1,13 +1,18 @@
 package jalview.gui;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
 import jalview.analysis.AlignmentGenerator;
 import jalview.bin.Cache;
 import jalview.bin.Jalview;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceGroup;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
 
+import java.awt.event.MouseEvent;
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintStream;
@@ -15,9 +20,11 @@ import java.io.PrintStream;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import junit.extensions.PA;
+
 public class FreeUpMemoryTest
 {
-  private static final int ONE_MB = 1024 * 1024;
+  private static final int ONE_MB = 1000 * 1000;
 
   /**
    * Configure (read-only) Jalview property settings for test
@@ -27,16 +34,12 @@ public class FreeUpMemoryTest
   {
     Jalview.main(new String[] { "-nonews", "-props",
         "test/jalview/testProps.jvprops" });
-    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
-            Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("SHOW_QUALITY",
-            Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
-            Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("SHOW_OCCUPANCY",
-            Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
-            Boolean.TRUE.toString());
+    String True = Boolean.TRUE.toString();
+    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", True);
+    Cache.applicationProperties.setProperty("SHOW_QUALITY", True);
+    Cache.applicationProperties.setProperty("SHOW_CONSERVATION", True);
+    Cache.applicationProperties.setProperty("SHOW_OCCUPANCY", True);
+    Cache.applicationProperties.setProperty("SHOW_IDENTITY", True);
   }
 
   /**
@@ -50,12 +53,12 @@ public class FreeUpMemoryTest
    * </li>
    * </ul>
    * If the test fails, this suggests that a reference to some large object
-   * (perhaps the alignment data, or consensus profile) has failed to be garbage
-   * collected. If this is the case, the heap will need to be inspected manually
-   * (suggest using jvisualvm) in order to track down where large objects are
-   * still referenced. The code (for example AlignmentViewport.dispose()) should
-   * then be updated to ensure references to large objects are set to null when
-   * they are no longer required.
+   * (perhaps the alignment data, or some annotation / Tree / PCA data) has
+   * failed to be garbage collected. If this is the case, the heap will need to
+   * be inspected manually (suggest using jvisualvm) in order to track down
+   * where large objects are still referenced. The code (for example
+   * AlignmentViewport.dispose()) should then be updated to ensure references to
+   * large objects are set to null when they are no longer required.
    * 
    * @throws IOException
    */
@@ -65,29 +68,35 @@ public class FreeUpMemoryTest
     File f = generateAlignment();
     f.deleteOnExit();
 
+    doStuffInJalview(f);
+
+    Desktop.instance.closeAll_actionPerformed(null);
+
+    checkUsedMemory(35L);
+  }
+
+  /**
+   * Requests garbage collection and then checks whether remaining memory in use
+   * is less than the expected value (in Megabytes)
+   * 
+   * @param expectedMax
+   */
+  protected void checkUsedMemory(long expectedMax)
+  {
     /*
-     * load alignment, wait for consensus and other threads to complete
+     * request garbage collection and wait for it to run;
+     * NB there is no guarantee when, or whether, it will do so
+     * wait time depends on JRE/processor, generous allowance here  
      */
-    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(f.getPath(),
-            DataSourceType.FILE);
-    waitForThreads(af.getViewport());
-
-    af.closeMenuItem_actionPerformed(true);
+    System.gc();
+    waitFor(1500);
 
     /*
-     * request garbage collection and allow 1 second for it to complete;
-     * NB there is no guarantee when, or whether, it will run!
+     * a second gc() call should not be necessary - but it is!
+     * the test passes with it, and fails without it
      */
     System.gc();
-    synchronized (this)
-    {
-      try
-      {
-        wait(1000);
-      } catch (InterruptedException e)
-      {
-      }
-    }
+    waitFor(1500);
 
     /*
      * check used memory is 'reasonably low'
@@ -95,8 +104,12 @@ public class FreeUpMemoryTest
     long availableMemory = Runtime.getRuntime().totalMemory() / ONE_MB;
     long freeMemory = Runtime.getRuntime().freeMemory() / ONE_MB;
     long usedMemory = availableMemory - freeMemory;
-    System.out.println("Memory in use after close all windows: "
-            + usedMemory + "MB");
+
+    /*
+     * sanity check - fails if any frame was added after
+     * closeAll_actionPerformed
+     */
+    assertEquals(Desktop.instance.getAllFrames().length, 0);
 
     /*
      * if this assertion fails
@@ -105,32 +118,102 @@ public class FreeUpMemoryTest
      * - identify large objects in the heap and their referers
      * - fix code as necessary to null the references on close
      */
-    long expectedMax = 100L;
-    assertTrue(usedMemory < expectedMax,
-            String.format("Used memory %d > %d", usedMemory, expectedMax));
+    System.out.println("Used memory after gc = " + usedMemory + "MB");
+    assertTrue(usedMemory < expectedMax, String.format(
+            "Used memory %d should be less than %d (Recommend running test manually to verify)",
+            usedMemory,
+            expectedMax));
   }
 
   /**
-   * wait for consensus etc thread to complete
+   * Loads an alignment from file and exercises various operations in Jalview
    * 
-   * @param av
+   * @param f
    */
-  protected void waitForThreads(AlignViewport av)
+  protected void doStuffInJalview(File f)
   {
-    while (av.isCalcInProgress())
+    /*
+     * load alignment, wait for consensus and other threads to complete
+     */
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(f.getPath(),
+            DataSourceType.FILE);
+    while (af.getViewport().isCalcInProgress())
+    {
+      waitFor(200);
+    }
+
+    /*
+     * open an Overview window
+     */
+    af.overviewMenuItem_actionPerformed(null);
+    assertNotNull(af.alignPanel.overviewPanel);
+
+    /*
+     * exercise the pop-up menu in the Overview Panel (JAL-2864)
+     */
+    Object[] args = new Object[] {
+        new MouseEvent(af, 0, 0, 0, 0, 0, 1, true) };
+    PA.invokeMethod(af.alignPanel.overviewPanel,
+            "showPopupMenu(java.awt.event.MouseEvent)", args);
+
+    /*
+     * set a selection group - potential memory leak if it retains
+     * a reference to the alignment
+     */
+    SequenceGroup sg = new SequenceGroup();
+    sg.setStartRes(0);
+    sg.setEndRes(100);
+    AlignmentI al = af.viewport.getAlignment();
+    for (int i = 0; i < al.getHeight(); i++)
+    {
+      sg.addSequence(al.getSequenceAt(i), false);
+    }
+    af.viewport.setSelectionGroup(sg);
+
+    /*
+     * compute Tree and PCA (on all sequences, 100 columns)
+     */
+    af.openTreePcaDialog();
+    CalculationChooser dialog = af.alignPanel.getCalculationDialog();
+    dialog.openPcaPanel("BLOSUM62", dialog.getSimilarityParameters(true));
+    dialog.openTreePanel("BLOSUM62", dialog.getSimilarityParameters(false));
+
+    /*
+     * wait until Tree and PCA have been computed
+     */
+    while (af.viewport.getCurrentTree() == null
+            && dialog.getPcaPanel().isWorking())
+    {
+      waitFor(10);
+    }
+
+    /*
+     * give Swing time to add the PCA panel (?!?)
+     */
+    waitFor(100);
+  }
+
+  /**
+   * Wait for waitMs miliseconds
+   * 
+   * @param waitMs
+   */
+  protected void waitFor(int waitMs)
+  {
+    try
+    {
+      Thread.sleep(waitMs);
+    } catch (InterruptedException e)
     {
-      try
-      {
-        Thread.sleep(200);
-      } catch (Exception x)
-      {
-      }
     }
   }
 
   /**
-   * Generates an alignment (large enough for this test but not so large it is
-   * too slow or runs out of memory) and saves it in a temporary file.
+   * Generates an alignment and saves it in a temporary file, to be loaded by
+   * Jalview. We use a peptide alignment (so Conservation and Quality are
+   * calculated), which is wide enough to ensure Consensus, Conservation and
+   * Occupancy have a significant memory footprint (if not removed from the
+   * heap).
    * 
    * @return
    * @throws IOException
@@ -140,7 +223,9 @@ public class FreeUpMemoryTest
     File f = File.createTempFile("MemoryTest", "fa");
     PrintStream ps = new PrintStream(f);
     AlignmentGenerator ag = new AlignmentGenerator(false, ps);
-    ag.generate(1000, 20000, 0, 10, 15);
+    int width = 100000;
+    int height = 100;
+    ag.generate(width, height, 0, 10, 15);
     return f;
   }
 }