JAL-2727 add SelectionGroup, Tree and PCA to test
[jalview.git] / test / jalview / gui / FreeUpMemoryTest.java
1 package jalview.gui;
2
3 import static org.testng.Assert.assertEquals;
4 import static org.testng.Assert.assertTrue;
5
6 import jalview.analysis.AlignmentGenerator;
7 import jalview.bin.Cache;
8 import jalview.bin.Jalview;
9 import jalview.datamodel.AlignmentI;
10 import jalview.datamodel.SequenceGroup;
11 import jalview.io.DataSourceType;
12 import jalview.io.FileLoader;
13
14 import java.io.File;
15 import java.io.IOException;
16 import java.io.PrintStream;
17
18 import org.testng.annotations.BeforeClass;
19 import org.testng.annotations.Test;
20
21 public class FreeUpMemoryTest
22 {
23   private static final int ONE_MB = 1000 * 1000;
24
25   /**
26    * Configure (read-only) Jalview property settings for test
27    */
28   @BeforeClass(alwaysRun = true)
29   public void setUp()
30   {
31     Jalview.main(new String[] { "-nonews", "-props",
32         "test/jalview/testProps.jvprops" });
33     Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
34             Boolean.TRUE.toString());
35     Cache.applicationProperties.setProperty("SHOW_QUALITY",
36             Boolean.TRUE.toString());
37     Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
38             Boolean.TRUE.toString());
39     Cache.applicationProperties.setProperty("SHOW_OCCUPANCY",
40             Boolean.TRUE.toString());
41     Cache.applicationProperties.setProperty("SHOW_IDENTITY",
42             Boolean.TRUE.toString());
43   }
44
45   /**
46    * A simple test that memory is released when all windows are closed.
47    * <ul>
48    * <li>generates a reasonably large alignment and loads it</li>
49    * <li>performs various operations on the alignment</li>
50    * <li>closes all windows</li>
51    * <li>requests garbage collection</li>
52    * <li>asserts that the remaining memory footprint (heap usage) is 'not large'
53    * </li>
54    * </ul>
55    * If the test fails, this suggests that a reference to some large object
56    * (perhaps the alignment data, or some annotation / Tree / PCA data) has
57    * failed to be garbage collected. If this is the case, the heap will need to
58    * be inspected manually (suggest using jvisualvm) in order to track down
59    * where large objects are still referenced. The code (for example
60    * AlignmentViewport.dispose()) should then be updated to ensure references to
61    * large objects are set to null when they are no longer required.
62    * 
63    * @throws IOException
64    */
65   @Test(groups = "Memory")
66   public void testFreeMemoryOnClose() throws IOException
67   {
68     File f = generateAlignment();
69     f.deleteOnExit();
70
71     doStuffInJalview(f);
72
73     Desktop.instance.closeAll_actionPerformed(null);
74
75     checkUsedMemory(35L);
76   }
77
78   /**
79    * Requests garbage collection and then checks whether remaining memory in use
80    * is less than the expected value (in Megabytes)
81    * 
82    * @param expectedMax
83    */
84   protected void checkUsedMemory(long expectedMax)
85   {
86     /*
87      * request garbage collection and wait briefly for it to run;
88      * NB there is no guarantee when, or whether, it will do so
89      */
90     System.gc();
91     waitFor(100);
92
93     /*
94      * a second gc() call should not be necessary - but it is!
95      * the test passes with it, and fails without it
96      */
97     System.gc();
98     waitFor(100);
99
100     /*
101      * check used memory is 'reasonably low'
102      */
103     long availableMemory = Runtime.getRuntime().totalMemory() / ONE_MB;
104     long freeMemory = Runtime.getRuntime().freeMemory() / ONE_MB;
105     long usedMemory = availableMemory - freeMemory;
106
107     /*
108      * sanity check - fails if any frame was added after
109      * closeAll_actionPerformed
110      */
111     assertEquals(Desktop.instance.getAllFrames().length, 0);
112
113     /*
114      * if this assertion fails
115      * - set a breakpoint here
116      * - run jvisualvm to inspect a heap dump of Jalview
117      * - identify large objects in the heap and their referers
118      * - fix code as necessary to null the references on close
119      */
120     System.out.println("Used memory after gc = " + usedMemory + "MB");
121     assertTrue(usedMemory < expectedMax, String.format(
122             "Used memory %d should be less than %d", usedMemory,
123             expectedMax));
124   }
125
126   /**
127    * Loads an alignment from file and exercises various operations in Jalview
128    * 
129    * @param f
130    */
131   protected void doStuffInJalview(File f)
132   {
133     /*
134      * load alignment, wait for consensus and other threads to complete
135      */
136     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(f.getPath(),
137             DataSourceType.FILE);
138     while (af.getViewport().isCalcInProgress())
139     {
140       waitFor(200);
141     }
142
143     /*
144      * set a selection group - potential memory leak if it retains
145      * a reference to the alignment
146      */
147     SequenceGroup sg = new SequenceGroup();
148     sg.setStartRes(0);
149     sg.setEndRes(100);
150     AlignmentI al = af.viewport.getAlignment();
151     for (int i = 0; i < al.getHeight(); i++)
152     {
153       sg.addSequence(al.getSequenceAt(i), false);
154     }
155     af.viewport.setSelectionGroup(sg);
156
157     /*
158      * compute Tree and PCA (on all sequences, 100 columns)
159      */
160     af.openTreePcaDialog();
161     CalculationChooser dialog = af.alignPanel.getCalculationDialog();
162     dialog.openPcaPanel("BLOSUM62", dialog.getSimilarityParameters(true));
163     dialog.openTreePanel("BLOSUM62", dialog.getSimilarityParameters(false));
164
165     /*
166      * wait until Tree and PCA have been computed
167      */
168     while (af.viewport.getCurrentTree() == null
169             && dialog.getPcaPanel().isWorking())
170     {
171       waitFor(10);
172     }
173
174     /*
175      * give Swing time to add the PCA panel (?!?)
176      */
177     waitFor(100);
178   }
179
180   /**
181    * Wait for waitMs miliseconds
182    * 
183    * @param waitMs
184    */
185   protected void waitFor(int waitMs)
186   {
187     try
188     {
189       Thread.sleep(waitMs);
190     } catch (InterruptedException e)
191     {
192     }
193   }
194
195   /**
196    * Generates an alignment and saves it in a temporary file, to be loaded by
197    * Jalview. We use a peptide alignment (so Conservation and Quality are
198    * calculated), which is wide enough to ensure Consensus, Conservation and
199    * Occupancy have a significant memory footprint (if not removed from the
200    * heap).
201    * 
202    * @return
203    * @throws IOException
204    */
205   private File generateAlignment() throws IOException
206   {
207     File f = File.createTempFile("MemoryTest", "fa");
208     PrintStream ps = new PrintStream(f);
209     AlignmentGenerator ag = new AlignmentGenerator(false, ps);
210     int width = 100000;
211     int height = 100;
212     ag.generate(width, height, 0, 10, 15);
213     return f;
214   }
215 }