import jalview.gui.JvOptionPane;
import jalview.io.FastaFile;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
import java.util.Arrays;
import java.util.Random;
import org.testng.annotations.BeforeClass;
/**
- * Generates, and outputs in Fasta format, a random DNA alignment for given
+ * Generates, and outputs in Fasta format, a random peptide or nucleotide alignment for given
* sequence length and count. Will regenerate the same alignment each time if
* the same random seed is used (so may be used for reproducible unit tests).
* Not guaranteed to reproduce the same results between versions, as the rules
* may get tweaked to produce more 'realistic' results.
*
- * Arguments:
- * <ul>
- * <li>length (number of bases in each sequence)</li>
- * <li>height (number of sequences)</li>
- * <li>a whole number random seed</li>
- * <li>percentage of gaps to include (0-100)</li>
- * <li>percentage chance of variation of each position (0-100)</li>
- * </ul>
- *
* @author gmcarstairs
- *
*/
public class AlignmentGenerator
{
- @BeforeClass(alwaysRun = true)
- public void setUpJvOptionPane()
- {
- JvOptionPane.setInteractiveMode(false);
- JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
- }
-
private static final char GAP = '-';
private static final char ZERO = '0';
private Random random;
+ private PrintStream ps;
/**
- * Outputs a DNA 'alignment' where each position is a random choice from
- * 'GTCA-'.
+ * Outputs a pseudo-randomly generated nucleotide or peptide alignment
+ * Arguments:
+ * <ul>
+ * <li>n (for nucleotide) or p (for peptide)</li>
+ * <li>length (number of bases in each sequence)</li>
+ * <li>height (number of sequences)</li>
+ * <li>a whole number random seed</li>
+ * <li>percentage of gaps to include (0-100)</li>
+ * <li>percentage chance of variation of each position (0-100)</li>
+ * <li>(optional) path to a file to write the alignment to</li>
+ * </ul>
+ *
*
* @param args
+ * @throws FileNotFoundException
*/
- public static void main(String[] args)
+ public static void main(String[] args) throws FileNotFoundException
{
- if (args.length != 6)
+ if (args.length != 6 && args.length != 7)
{
usage();
return;
}
+
+ PrintStream ps = System.out;
+ if (args.length == 7)
+ {
+ ps = new PrintStream(new File(args[6]));
+ }
+
boolean nucleotide = args[0].toLowerCase().startsWith("n");
int width = Integer.parseInt(args[1]);
int height = Integer.parseInt(args[2]);
long randomSeed = Long.valueOf(args[3]);
int gapPercentage = Integer.valueOf(args[4]);
int changePercentage = Integer.valueOf(args[5]);
- AlignmentI al = new AlignmentGenerator(nucleotide).generate(width,
- height,
- randomSeed, gapPercentage, changePercentage);
- System.out.println("; " + height + " sequences of " + width
+ ps.println("; " + height + " sequences of " + width
+ " bases with " + gapPercentage + "% gaps and "
+ changePercentage + "% mutations (random seed = " + randomSeed
+ ")");
- System.out.println(new FastaFile().print(al.getSequencesArray(), true));
+
+ new AlignmentGenerator(nucleotide, ps).generate(width, height,
+ randomSeed, gapPercentage, changePercentage);
+
+ if (ps != System.out)
+ {
+ ps.close();
+ }
}
/**
- * Print parameter help.
+ * Prints parameter help
*/
private static void usage()
{
System.out.println("Usage:");
System.out.println("arg0: n (for nucleotide) or p (for peptide)");
System.out.println("arg1: number of (non-gap) bases per sequence");
- System.out.println("arg2: number sequences");
+ System.out.println("arg2: number of sequences");
System.out
.println("arg3: an integer as random seed (same seed = same results)");
System.out.println("arg4: percentage of gaps to (randomly) generate");
System.out
.println("arg5: percentage of 'mutations' to (randomly) generate");
+ System.out
+ .println("arg6: (optional) path to output file (default is sysout)");
System.out.println("Example: AlignmentGenerator n 12 15 387 10 5");
System.out
.println("- 15 nucleotide sequences of 12 bases each, approx 10% gaps and 5% mutations, random seed = 387");
}
/**
- * Constructor that sets nucleotide or peptide symbol set
+ * Constructor that sets nucleotide or peptide symbol set, and also writes the
+ * generated alignment to sysout
*/
public AlignmentGenerator(boolean nuc)
{
- BASES = nuc ? NUCS : PEPS;
+ this(nuc, System.out);
+ }
+
+ /**
+ * Constructor that sets nucleotide or peptide symbol set, and also writes the
+ * generated alignment to the specified output stream (if not null). This can
+ * be used to write the alignment to a file or sysout.
+ */
+ public AlignmentGenerator(boolean nucleotide, PrintStream printStream)
+ {
+ BASES = nucleotide ? NUCS : PEPS;
+ ps = printStream;
}
/**
- * Outputs a DNA 'alignment' of given width and height, where each position is
- * a random choice from 'GTCA-'.
+ * Outputs an 'alignment' of given width and height, where each position is a
+ * random choice from the symbol alphabet, or - for gap
*
* @param width
* @param height
seqno + 1, width, changePercentage);
}
AlignmentI al = new Alignment(seqs);
+
+ if (ps != null)
+ {
+ ps.println(new FastaFile().print(al.getSequencesArray(), true));
+ }
+
return al;
}
--- /dev/null
+package jalview.gui;
+
+import static org.testng.Assert.assertTrue;
+
+import jalview.analysis.AlignmentGenerator;
+import jalview.bin.Cache;
+import jalview.bin.Jalview;
+import jalview.io.DataSourceType;
+import jalview.io.FileLoader;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class FreeUpMemoryTest
+{
+ private static final int ONE_MB = 1024 * 1024;
+
+ /**
+ * Configure (read-only) Jalview property settings for test
+ */
+ @BeforeClass(alwaysRun = true)
+ public void setUp()
+ {
+ 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());
+ }
+
+ /**
+ * A simple test that memory is released when all windows are closed.
+ * <ul>
+ * <li>generates a reasonably large alignment and loads it</li>
+ * <li>performs various operations on the alignment</li>
+ * <li>closes all windows</li>
+ * <li>requests garbage collection</li>
+ * <li>asserts that the remaining memory footprint (heap usage) is 'not large'
+ * </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.
+ *
+ * @throws IOException
+ */
+ @Test(groups = "Memory")
+ public void testFreeMemoryOnClose() 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());
+
+ af.closeMenuItem_actionPerformed(true);
+
+ /*
+ * request garbage collection and allow 1 second for it to complete;
+ * NB there is no guarantee when, or whether, it will run!
+ */
+ System.gc();
+ synchronized (this)
+ {
+ try
+ {
+ wait(1000);
+ } catch (InterruptedException e)
+ {
+ }
+ }
+
+ /*
+ * 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");
+
+ /*
+ * if this assertion fails
+ * - set a breakpoint here
+ * - run jvisualvm to inspect a heap dump of Jalview
+ * - 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));
+ }
+
+ /**
+ * wait for consensus etc thread to complete
+ *
+ * @param av
+ */
+ protected void waitForThreads(AlignViewport av)
+ {
+ while (av.isCalcInProgress())
+ {
+ 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.
+ *
+ * @return
+ * @throws IOException
+ */
+ private File generateAlignment() throws IOException
+ {
+ File f = File.createTempFile("MemoryTest", "fa");
+ PrintStream ps = new PrintStream(f);
+ AlignmentGenerator ag = new AlignmentGenerator(false, ps);
+ ag.generate(1000, 20000, 0, 10, 15);
+ return f;
+ }
+}