// deleting old backup/version files
private static boolean confirmDelete;
- private static boolean classInit = false;
-
// defaultSuffix - default template to use to append to basename of file
private String suffix;
public static void classInit()
{
- if (!classInit)
- {
- setEnabled(Cache.getDefault(ENABLED, true));
- setConfirmDelete(Cache.getDefault(CONFIRM_DELETE_OLD, true));
- classInit = true;
- }
+ setEnabled(Cache.getDefault(ENABLED, true));
+ setConfirmDelete(Cache.getDefault(CONFIRM_DELETE_OLD, true));
}
public static void setEnabled(boolean flag)
return rename;
}
+ public static TreeMap<Integer, File> lsBackupFilesAsTreeMap(
+ String fileName,
+ String suffix, int digits)
+ {
+ File[] backupFiles = null;
+
+ File file = new File(fileName);
+
+ String dir = "";
+ File dirFile;
+ try
+ {
+ dirFile = file.getParentFile();
+ dir = dirFile.getCanonicalPath();
+ } catch (Exception e)
+ {
+ System.out.println(
+ "Could not get canonical path for file '" + file + "'");
+ return new TreeMap<>();
+ }
+
+ String filename = file.getName();
+ String basename = filename;
+ String extension = "";
+ int dotcharpos = filename.lastIndexOf('.');
+ // don't split of filenames with the last '.' at the very beginning or
+ // very end of the filename
+ if ((dotcharpos > 0) && (dotcharpos < filename.length() - 1))
+ {
+ basename = filename.substring(0, dotcharpos);
+ extension = filename.substring(dotcharpos); // NOTE this includes the '.'
+ }
+
+ // find existing backup files
+ BackupFilenameFilter bff = new BackupFilenameFilter(basename, suffix, digits, extension);
+ backupFiles = dirFile.listFiles(bff); // is clone needed?
+
+ // sort the backup files (based on integer found in the suffix) using a
+ // precomputed Hashmap for speed
+ HashMap<Integer, File> bfHashMap = new HashMap<>();
+ for (int i = 0; i < backupFiles.length; i++)
+ {
+ File f = backupFiles[i];
+ BackupFilenameParts bfp = new BackupFilenameParts(f, basename, suffix,
+ digits, extension);
+ bfHashMap.put(bfp.indexNum(), f);
+ }
+ TreeMap<Integer, File> bfTreeMap = new TreeMap<>();
+ bfTreeMap.putAll(bfHashMap);
+
+ return bfTreeMap;
+ }
+
+ public static File[] lsBackupFiles(String fileName, String suffix,
+ int digits)
+ {
+ TreeMap<Integer, File> bfTreeMap = lsBackupFilesAsTreeMap(fileName,
+ suffix, digits);
+ File[] backupFiles = new File[bfTreeMap.size()];
+ bfTreeMap.values().toArray(backupFiles);
+ return backupFiles;
+ }
+
}
--- /dev/null
+package jalview.io;
+
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.JvOptionPane;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.TreeMap;
+
+import org.junit.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class BackupFilesTest
+{
+ @BeforeClass(alwaysRun = true)
+ public void setUpJvOptionPane()
+ {
+ JvOptionPane.setInteractiveMode(false);
+ JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+ }
+
+ private static boolean actuallyDeleteTmpFiles = true;
+ private static String testDir = "examples";
+
+ private static String testFilename = "backupfilestest.fa";
+
+
+ private static String testFile = testDir + File.separatorChar
+ + testFilename;
+
+ private static String newFilename = "backupfilestestTemp.fa";
+
+ private static String newFile = testDir + File.separatorChar
+ + newFilename;
+
+ private static String sequenceName = "BACKUP_FILES";
+
+ private static String sequenceDescription = "backupfiles";
+
+ private static String sequenceData = "AAAARG";
+
+ private static String suffix = "_BACKUPTEST-%n";
+
+ private static int digits = 8;
+
+ private static int rollMax = 2;
+
+ private AlignFrame af;
+
+ // read and save with backupfiles disabled
+ @Test(groups = { "Functional" })
+ public void noBackupsEnabledTest() throws Exception
+ {
+ // set BACKUPFILES_ENABLED to false (i.e. turn off BackupFiles feature -- no
+ // backup files to be made when saving)
+ setBackupFilesOptions(false, true, true);
+
+ // init the newFile and backups (i.e. make sure newFile exists on its own
+ // and has no backups)
+ initNewFileForTesting();
+
+ // now save again
+ save();
+
+ // check no backup files
+ File[] backupFiles = getBackupFiles();
+ Assert.assertTrue(backupFiles.length == 0);
+ }
+
+ // save keeping all backup files
+ @Test(groups = { "Functional" })
+ public void backupsEnabledNoRollMaxTest() throws Exception
+ {
+ // Enable BackupFiles and set noMax so all backupfiles get kept
+ setBackupFilesOptions(true, false, true);
+
+ // init the newFile and backups (i.e. make sure newFile exists on its own
+ // and has no backups)
+ initNewFileForTesting();
+
+ // now save a few times again. No rollMax so should have more than two
+ // backup files
+ int numSaves = 10;
+ for (int i = 0; i < numSaves; i++)
+ {
+ save();
+ }
+
+ // check 10 backup files
+ int[] indexes1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int[] indexes2 = { 3, 4, 5, 6, 7, 8, 9, 10 };
+ int[] indexes3 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
+ Assert.assertTrue(checkBackupFiles(indexes1));
+ Assert.assertFalse(checkBackupFiles(indexes2));
+ Assert.assertFalse(checkBackupFiles(indexes3));
+ }
+
+ // save keeping only the last rollMax (2) backup files
+ @Test(groups = { "Functional" })
+ public void backupsEnabledRollMaxTest() throws Exception
+ {
+ // Enable BackupFiles and set noMax so all backupfiles get kept
+ setBackupFilesOptions(true, false, false);
+
+ // init the newFile and backups (i.e. make sure newFile exists on its own
+ // and has no backups)
+ initNewFileForTesting();
+
+ // now save a few times again. No rollMax so should have more than two
+ // backup files
+ int numSaves = 10;
+ for (int i = 0; i < numSaves; i++)
+ {
+ save();
+ }
+
+ // check there are "rollMax" backup files and they are all saved correctly
+ // check 10 backup files
+ int[] indexes1 = { 9, 10 };
+ int[] indexes2 = { 10 };
+ int[] indexes3 = { 8, 9, 10 };
+ Assert.assertTrue(checkBackupFiles(indexes1));
+ Assert.assertFalse(checkBackupFiles(indexes2));
+ Assert.assertFalse(checkBackupFiles(indexes3));
+ }
+
+ // save keeping only the last rollMax (2) backup files
+ @Test(groups = { "Functional" })
+ public void backupsEnabledReverseRollMaxTest() throws Exception
+ {
+ // Enable BackupFiles and set noMax so all backupfiles get kept
+ setBackupFilesOptions(true, true, false);
+
+ // init the newFile and backups (i.e. make sure newFile exists on its own
+ // and has no backups)
+ initNewFileForTesting();
+
+ // now save a few times again. No rollMax so should have more than two
+ // backup files
+ int numSaves = 10;
+ for (int i = 0; i < numSaves; i++)
+ {
+ save();
+ }
+
+ // check there are "rollMax" backup files and they are all saved correctly
+ // check 10 backup files
+ int[] indexes1 = { 1, 2 };
+ int[] indexes2 = { 1 };
+ int[] indexes3 = { 1, 2, 3 };
+ Assert.assertTrue(checkBackupFiles(indexes1));
+ Assert.assertFalse(checkBackupFiles(indexes2));
+ Assert.assertFalse(checkBackupFiles(indexes3));
+ }
+
+ private void setBackupFilesOptions()
+ {
+ setBackupFilesOptions(true, false, false);
+ }
+
+ private void setBackupFilesOptions(boolean enabled, boolean reverse,
+ boolean noMax)
+ {
+ Cache.loadProperties("test/jalview/io/testProps.jvprops");
+
+ Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
+ Boolean.toString(enabled));
+ Cache.applicationProperties.setProperty(BackupFiles.SUFFIX, suffix);
+ Cache.applicationProperties.setProperty(BackupFiles.SUFFIX_DIGITS,
+ Integer.toString(digits));
+ Cache.applicationProperties.setProperty(BackupFiles.REVERSE_ORDER,
+ Boolean.toString(reverse));
+ Cache.applicationProperties.setProperty(BackupFiles.NO_MAX,
+ Boolean.toString(noMax));
+ Cache.applicationProperties.setProperty(BackupFiles.ROLL_MAX,
+ Integer.toString(rollMax));
+ Cache.applicationProperties.setProperty(BackupFiles.CONFIRM_DELETE_OLD,
+ "false");
+ }
+
+ private void save()
+ {
+ if (af != null)
+ {
+ af.saveAlignment(newFile, jalview.io.FileFormat.Fasta);
+ }
+ }
+
+ // this runs cleanTmpFiles and then writes the newFile once as a starting
+ // point for all tests
+ private void initNewFileForTesting() throws Exception
+ {
+ cleanupTmpFiles();
+
+ AppletFormatAdapter afa = new AppletFormatAdapter();
+ AlignmentI al = afa.readFile(testFile, DataSourceType.FILE,
+ jalview.io.FileFormat.Fasta);
+ List<SequenceI> l = al.getSequences();
+
+ // check this is right
+ if (l.size() != 1)
+ {
+ throw new Exception("single sequence from '" + testFile
+ + "' not read in correctly (should be a single short sequence). List<SequenceI> size is wrong.");
+ }
+ SequenceI s = l.get(0);
+ Sequence ref = new Sequence(sequenceName, sequenceData);
+ ref.setDescription(sequenceDescription);
+ if (!sequencesEqual(s, ref))
+ {
+ throw new Exception("single sequence from '" + testFile
+ + "' not read in correctly (should be a single short sequence). SequenceI name, description or data is wrong.");
+ }
+ // save alignment file to new filename -- this doesn't test backups disabled
+ // yet as this file shouldn't already exist
+ af = new AlignFrame(al, 0, 0);
+ af.saveAlignment(newFile, jalview.io.FileFormat.Fasta);
+ }
+
+ // this deletes the newFile (if it exists) and any saved backup file for it
+ @AfterClass(alwaysRun = true)
+ private void cleanupTmpFiles()
+ {
+ File newfile = new File(newFile);
+ if (newfile.exists())
+ {
+ newfile.delete();
+ }
+ File[] tmpFiles = BackupFiles.lsBackupFiles(newFile, suffix, digits);
+ for (int i = 0; i < tmpFiles.length; i++)
+ {
+ if (actuallyDeleteTmpFiles)
+ {
+ tmpFiles[i].delete();
+ }
+ else
+ {
+ System.out.println("Pretending to delete " + tmpFiles[i].getPath());
+ }
+ }
+ }
+
+ private static File[] getBackupFiles()
+ {
+ return BackupFiles.lsBackupFiles(newFile, suffix, digits);
+ }
+
+ private static boolean checkBackupFiles(int[] indexes) throws IOException
+ {
+ TreeMap<Integer, File> map = BackupFiles.lsBackupFilesAsTreeMap(newFile,
+ suffix, digits);
+ for (int m = 0; m < indexes.length; m++)
+ {
+ int i = indexes[m];
+ if (!map.containsKey(i))
+ {
+ return false;
+ }
+ File f = map.get(i);
+ if (!filesContentEqual(newFile, f.getPath()))
+ {
+ return false;
+ }
+ map.remove(i);
+ if (f == null)
+ {
+ return false;
+ }
+ }
+ // should be nothing left in map
+ if (map.size() > 0)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private static String[] getBackupFilesAsStrings()
+ {
+ File[] files = BackupFiles.lsBackupFiles(newFile, suffix, digits);
+ String[] filenames = new String[files.length];
+ for (int i = 0; i < files.length; i++)
+ {
+ filenames[i] = files[i].getPath();
+ }
+ return filenames;
+ }
+
+ public static boolean sequencesEqual(SequenceI s1, SequenceI s2) {
+ if (s1 == null && s2 == null) {
+ return true;
+ } else if (s1 == null || s2 == null) {
+ return false;
+ }
+ return (s1.getName().equals(s2.getName())
+ && s1.getDescription().equals(s2.getDescription())
+ && Arrays.equals(s1.getSequence(), s2.getSequence()));
+ }
+
+ public static boolean filesContentEqual(String fileName1,
+ String fileName2) throws IOException
+ {
+ Path file1 = Paths.get(fileName1);
+ Path file2 = Paths.get(fileName2);
+ byte[] bytes1 = Files.readAllBytes(file1);
+ byte[] bytes2 = Files.readAllBytes(file2);
+ return Arrays.equals(bytes1, bytes2);
+ }
+
+}