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;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import junit.extensions.PA;
+
public class FreeUpMemoryTest
{
private static final int ONE_MB = 1000 * 1000;
{
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);
}
/**
* </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
*/
File f = generateAlignment();
f.deleteOnExit();
- /*
- * load alignment, wait for consensus and other threads to complete
- */
- AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(f.getPath(),
- DataSourceType.FILE);
- waitForThreads(af.getViewport());
+ long expectedMin = 35L;
+ long usedMemoryAtStart=getUsedMemory();
+ if (usedMemoryAtStart>expectedMin)
+ {
+ System.err.println("used memory before test is "+usedMemoryAtStart+" > "+expectedMin+"MB .. adjusting minimum.");
+ expectedMin = usedMemoryAtStart;
+ }
+ doStuffInJalview(f);
- af.closeMenuItem_actionPerformed(true);
+ Desktop.instance.closeAll_actionPerformed(null);
+ checkUsedMemory(expectedMin);
+ }
+
+ private static long getUsedMemory()
+ {
+ long availableMemory = Runtime.getRuntime().totalMemory() / ONE_MB;
+ long freeMemory = Runtime.getRuntime().freeMemory() / ONE_MB;
+ long usedMemory = availableMemory - freeMemory;
+ return usedMemory;
+ }
+ /**
+ * 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)
+ {
/*
- * request garbage collection and wait briefly for it to run;
+ * 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
*/
System.gc();
- synchronized (this)
- {
- try
- {
- wait(10);
- } catch (InterruptedException e)
- {
- }
- }
+ waitFor(1500);
/*
* a second gc() call should not be necessary - but it is!
* the test passes with it, and fails without it
*/
System.gc();
+ waitFor(1500);
/*
* check used memory is 'reasonably low'
*/
- 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");
+ long usedMemory = getUsedMemory();
+ /*
+ * sanity check - fails if any frame was added after
+ * closeAll_actionPerformed
+ */
+ assertEquals(Desktop.instance.getAllFrames().length, 0);
/*
* if this assertion fails
* - identify large objects in the heap and their referers
* - fix code as necessary to null the references on close
*/
- long expectedMax = 30L; // typically reports around 25
+ System.out.println("Used memory after gc = " + usedMemory + "MB");
assertTrue(usedMemory < expectedMax, String.format(
- "Used memory %d should be less than %d", usedMemory,
+ "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 f
+ */
+ protected void doStuffInJalview(File f)
+ {
+ /*
+ * 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 av
+ * @param waitMs
*/
- protected void waitForThreads(AlignViewport av)
+ protected void waitFor(int waitMs)
{
- while (av.isCalcInProgress())
+ try
+ {
+ Thread.sleep(waitMs);
+ } catch (InterruptedException e)
{
- try
- {
- Thread.sleep(200);
- } catch (Exception x)
- {
- }
}
}