JAL-3628 Test for doubly saved single backup file scheme
[jalview.git] / test / jalview / io / BackupFilesTest.java
1 package jalview.io;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.nio.file.Files;
6 import java.nio.file.Path;
7 import java.nio.file.Paths;
8 import java.util.Arrays;
9 import java.util.Collections;
10 import java.util.Enumeration;
11 import java.util.HashMap;
12 import java.util.List;
13 import java.util.TreeMap;
14
15 import org.testng.Assert;
16 import org.testng.annotations.AfterClass;
17 import org.testng.annotations.BeforeClass;
18 import org.testng.annotations.Test;
19
20 import jalview.bin.Cache;
21 import jalview.datamodel.AlignmentI;
22 import jalview.datamodel.Sequence;
23 import jalview.datamodel.SequenceI;
24 import jalview.gui.AlignFrame;
25 import jalview.gui.JvOptionPane;
26
27 public class BackupFilesTest
28 {
29   @BeforeClass(alwaysRun = true)
30   public void setUpJvOptionPane()
31   {
32     JvOptionPane.setInteractiveMode(false);
33     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
34   }
35
36   private static boolean actuallyDeleteTmpFiles = true;
37
38   private static String testDir = "test/jalview/io";
39
40   private static String testBasename = "backupfilestest";
41
42   private static String testExt = ".fa";
43
44   private static String testFilename = testBasename + testExt;
45
46   private static String testFile = testDir + File.separatorChar
47           + testFilename;
48
49   private static String newBasename = testBasename + "Temp";
50
51   private static String newFilename = newBasename + testExt;
52
53   private static String newFile = testDir + File.separatorChar
54           + newFilename;
55
56   private static String sequenceName = "BACKUP_FILES";
57
58   private static String sequenceDescription = "backupfiles";
59
60   private static String sequenceData = "AAAARG";
61
62   private static String suffix = "_BACKUPTEST-%n";
63
64   private static int digits = 6;
65
66   private static int rollMax = 2;
67
68   private AlignFrame af;
69
70   // read and save with backupfiles disabled
71   @Test(groups = { "Functional" })
72   public void noBackupsEnabledTest() throws Exception
73   {
74     // set BACKUPFILES_ENABLED to false (i.e. turn off BackupFiles feature -- no
75     // backup files to be made when saving)
76     setBackupFilesOptions(false, true, true);
77
78     // init the newFile and backups (i.e. make sure newFile exists on its own
79     // and has no backups
80     initNewFileForTesting();
81
82     // now save again
83     save();
84
85     // check no backup files
86     File[] backupFiles = getBackupFiles();
87     Assert.assertTrue(backupFiles.length == 0);
88   }
89
90   // save with no numbers in the backup file names
91   @Test(groups = { "Functional" })
92   public void backupsEnabledSingleFileBackupTest() throws Exception
93   {
94     // Enable BackupFiles and set noMax so all backupfiles get kept
95     String mysuffix = "~";
96     BackupFilesPresetEntry bfpe = new BackupFilesPresetEntry(mysuffix, 1,
97             false, true, 1, false);
98     setBackupFilesOptions(true, false, true,
99             "test/jalview/io/testProps_singlefilebackup.jvprops", bfpe);
100
101     // init the newFile and backups (i.e. make sure newFile exists on its own
102     // and has no backups)
103     initNewFileForTesting();
104     HashMap<Integer, String> correctindexmap = new HashMap<>();
105     correctindexmap.put(0, "backupfilestestTemp.fa~");
106
107     save();
108     Assert.assertTrue(checkBackupFiles(correctindexmap, newFile, "~", 1));
109
110     // and a second time -- see JAL-3628
111     save();
112     Assert.assertTrue(checkBackupFiles(correctindexmap, newFile, "~", 1));
113
114     cleanupTmpFiles(newFile, "~", 1);
115   }
116
117   // save keeping all backup files
118   @Test(groups = { "Functional" })
119   public void backupsEnabledNoRollMaxTest() throws Exception
120   {
121     // Enable BackupFiles and set noMax so all backupfiles get kept
122     setBackupFilesOptions(true, false, true);
123
124     // init the newFile and backups (i.e. make sure newFile exists on its own
125     // and has no backups)
126     initNewFileForTesting();
127
128     // now save a few times again. No rollMax so should have more than two
129     // backup files
130     int numSaves = 10;
131     for (int i = 0; i < numSaves; i++)
132     {
133       save();
134     }
135
136     // check 10 backup files
137     HashMap<Integer, String> correctindexmap = new HashMap<>();
138     correctindexmap.put(1, "backupfilestestTemp.fa_BACKUPTEST-000001");
139     correctindexmap.put(2, "backupfilestestTemp.fa_BACKUPTEST-000002");
140     correctindexmap.put(3, "backupfilestestTemp.fa_BACKUPTEST-000003");
141     correctindexmap.put(4, "backupfilestestTemp.fa_BACKUPTEST-000004");
142     correctindexmap.put(5, "backupfilestestTemp.fa_BACKUPTEST-000005");
143     correctindexmap.put(6, "backupfilestestTemp.fa_BACKUPTEST-000006");
144     correctindexmap.put(7, "backupfilestestTemp.fa_BACKUPTEST-000007");
145     correctindexmap.put(8, "backupfilestestTemp.fa_BACKUPTEST-000008");
146     correctindexmap.put(9, "backupfilestestTemp.fa_BACKUPTEST-000009");
147     correctindexmap.put(10, "backupfilestestTemp.fa_BACKUPTEST-000010");
148     HashMap<Integer, String> wrongindexmap = new HashMap<>();
149     wrongindexmap.put(1, "backupfilestestTemp.fa_BACKUPTEST-1");
150     wrongindexmap.put(2, "backupfilestestTemp.fa_BACKUPTEST-000002");
151     wrongindexmap.put(3, "backupfilestestTemp.fa_BACKUPTEST-000003");
152     wrongindexmap.put(4, "backupfilestestTemp.fa_BACKUPTEST-000004");
153     wrongindexmap.put(5, "backupfilestestTemp.fa_BACKUPTEST-000005");
154     wrongindexmap.put(6, "backupfilestestTemp.fa_BACKUPTEST-000006");
155     wrongindexmap.put(7, "backupfilestestTemp.fa_BACKUPTEST-000007");
156     wrongindexmap.put(8, "backupfilestestTemp.fa_BACKUPTEST-000008");
157     wrongindexmap.put(9, "backupfilestestTemp.fa_BACKUPTEST-000009");
158     wrongindexmap.put(10, "backupfilestestTemp.fa_BACKUPTEST-000010");
159     int[] indexes2 = { 3, 4, 5, 6, 7, 8, 9, 10 };
160     int[] indexes3 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
161     Assert.assertTrue(checkBackupFiles(correctindexmap));
162     Assert.assertFalse(checkBackupFiles(wrongindexmap));
163     Assert.assertFalse(checkBackupFiles(indexes2));
164     Assert.assertFalse(checkBackupFiles(indexes3));
165   }
166
167   // save keeping only the last rollMax (2) backup files
168   @Test(groups = { "Functional" })
169   public void backupsEnabledRollMaxTest() throws Exception
170   {
171     // Enable BackupFiles and set noMax so all backupfiles get kept
172     setBackupFilesOptions(true, false, false);
173
174     // init the newFile and backups (i.e. make sure newFile exists on its own
175     // and has no backups)
176     initNewFileForTesting();
177
178     // now save a few times again. No rollMax so should have more than two
179     // backup files
180     int numSaves = 10;
181     for (int i = 0; i < numSaves; i++)
182     {
183       save();
184     }
185
186     // check there are "rollMax" backup files and they are all saved correctly
187     // check 10 backup files
188     HashMap<Integer, String> correctindexmap = new HashMap<>();
189     correctindexmap.put(9, "backupfilestestTemp.fa_BACKUPTEST-000009");
190     correctindexmap.put(10, "backupfilestestTemp.fa_BACKUPTEST-000010");
191     int[] indexes2 = { 10 };
192     int[] indexes3 = { 8, 9, 10 };
193     Assert.assertTrue(checkBackupFiles(correctindexmap));
194     Assert.assertFalse(checkBackupFiles(indexes2));
195     Assert.assertFalse(checkBackupFiles(indexes3));
196   }
197
198   // save keeping only the last rollMax (2) backup files
199   @Test(groups = { "Functional" })
200   public void backupsEnabledReverseRollMaxTest() throws Exception
201   {
202     // Enable BackupFiles and set noMax so all backupfiles get kept
203     setBackupFilesOptions(true, true, false);
204
205     // init the newFile and backups (i.e. make sure newFile exists on its own
206     // and has no backups)
207     initNewFileForTesting();
208
209     // now save a few times again. No rollMax so should have more than two
210     // backup files
211     int numSaves = 10;
212     for (int i = 0; i < numSaves; i++)
213     {
214       save();
215     }
216
217     // check there are "rollMax" backup files and they are all saved correctly
218     // check 10 backup files
219     HashMap<Integer, String> correctindexmap = new HashMap<>();
220     correctindexmap.put(1, "backupfilestestTemp.fa_BACKUPTEST-000001");
221     correctindexmap.put(2, "backupfilestestTemp.fa_BACKUPTEST-000002");
222     int[] indexes2 = { 1 };
223     int[] indexes3 = { 1, 2, 3 };
224     Assert.assertTrue(checkBackupFiles(correctindexmap));
225     Assert.assertFalse(checkBackupFiles(indexes2));
226     Assert.assertFalse(checkBackupFiles(indexes3));
227   }
228
229   private void setBackupFilesOptions()
230   {
231     setBackupFilesOptions(true, false, false);
232   }
233
234   private void setBackupFilesOptions(boolean enabled, boolean reverse,
235           boolean noMax)
236   {
237     BackupFilesPresetEntry bfpe = new BackupFilesPresetEntry(suffix, digits,
238             reverse, noMax, rollMax, false);
239     setBackupFilesOptions(enabled, reverse, noMax,
240             "test/jalview/io/testProps.jvprops", bfpe);
241   }
242
243   private void setBackupFilesOptions(boolean enabled, boolean reverse,
244           boolean noMax, String propsFile, BackupFilesPresetEntry bfpe)
245   {
246     Cache.loadProperties(propsFile);
247
248     Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
249             Boolean.toString(enabled));
250     Cache.applicationProperties.setProperty(
251             BackupFilesPresetEntry.SAVEDCONFIG, bfpe.toString());
252     /*
253     Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
254             Boolean.toString(enabled));
255     Cache.applicationProperties.setProperty(BackupFiles.SUFFIX, suffix);
256     Cache.applicationProperties.setProperty(BackupFiles.SUFFIX_DIGITS,
257             Integer.toString(digits));
258     Cache.applicationProperties.setProperty(BackupFiles.REVERSE_ORDER,
259             Boolean.toString(reverse));
260     Cache.applicationProperties.setProperty(BackupFiles.NO_MAX,
261             Boolean.toString(noMax));
262     Cache.applicationProperties.setProperty(BackupFiles.ROLL_MAX,
263             Integer.toString(rollMax));
264     Cache.applicationProperties.setProperty(BackupFiles.CONFIRM_DELETE_OLD,
265             "false");
266             */
267   }
268
269   private void save()
270   {
271     if (af != null)
272     {
273       af.saveAlignment(newFile, jalview.io.FileFormat.Fasta);
274     }
275   }
276
277   // this runs cleanTmpFiles and then writes the newFile once as a starting
278   // point for all tests
279   private void initNewFileForTesting() throws Exception
280   {
281     cleanupTmpFiles();
282
283     AppletFormatAdapter afa = new AppletFormatAdapter();
284     AlignmentI al = afa.readFile(testFile, DataSourceType.FILE,
285             jalview.io.FileFormat.Fasta);
286     List<SequenceI> l = al.getSequences();
287
288     // check this is right
289     if (l.size() != 1)
290     {
291       throw new Exception("single sequence from '" + testFile
292               + "' not read in correctly (should be a single short sequence). List<SequenceI> size is wrong.");
293     }
294     SequenceI s = l.get(0);
295     Sequence ref = new Sequence(sequenceName, sequenceData);
296     ref.setDescription(sequenceDescription);
297     if (!sequencesEqual(s, ref))
298     {
299       throw new Exception("single sequence from '" + testFile
300               + "' not read in correctly (should be a single short sequence). SequenceI name, description or data is wrong.");
301     }
302     // save alignment file to new filename -- this doesn't test backups disabled
303     // yet as this file shouldn't already exist
304     af = new AlignFrame(al, 0, 0);
305     af.saveAlignment(newFile, jalview.io.FileFormat.Fasta);
306   }
307
308   // this deletes the newFile (if it exists) and any saved backup file for it
309   @AfterClass(alwaysRun = true)
310   private void cleanupTmpFiles()
311   {
312     cleanupTmpFiles(newFile, suffix, digits);
313   }
314
315   private void cleanupTmpFiles(String file, String mysuffix, int mydigits)
316   {
317     File newfile = new File(file);
318     if (newfile.exists())
319     {
320       newfile.delete();
321     }
322     File[] tmpFiles = getBackupFiles(file, mysuffix, mydigits);
323     for (int i = 0; i < tmpFiles.length; i++)
324     {
325       if (actuallyDeleteTmpFiles)
326       {
327         tmpFiles[i].delete();
328       }
329       else
330       {
331         System.out.println("Pretending to delete " + tmpFiles[i].getPath());
332       }
333     }
334   }
335
336   private static File[] getBackupFiles(String f, String s, int i)
337   {
338     TreeMap<Integer, File> bfTreeMap = BackupFiles
339             .getBackupFilesAsTreeMap(f, s, i);
340     File[] backupFiles = new File[bfTreeMap.size()];
341     bfTreeMap.values().toArray(backupFiles);
342     return backupFiles;
343   }
344
345   private static File[] getBackupFiles()
346   {
347     return getBackupFiles(newFile, suffix, digits);
348   }
349
350   private static boolean checkBackupFiles(HashMap<Integer, String> indexmap)
351           throws IOException
352   {
353     return checkBackupFiles(indexmap, newFile, suffix, digits);
354   }
355
356   private static boolean checkBackupFiles(HashMap<Integer, String> indexmap,
357           String file, String mysuffix, int mydigits) throws IOException
358   {
359     TreeMap<Integer, File> map = BackupFiles.getBackupFilesAsTreeMap(file,
360             mysuffix, mydigits);
361     Enumeration<Integer> indexesenum = Collections
362             .enumeration(indexmap.keySet());
363     while (indexesenum.hasMoreElements())
364     {
365       int i = indexesenum.nextElement();
366       String indexfilename = indexmap.get(i);
367       if (!map.containsKey(i))
368       {
369         return false;
370       }
371       File f = map.get(i);
372       if (!filesContentEqual(newFile, f.getPath()))
373       {
374         return false;
375       }
376       map.remove(i);
377       if (f == null)
378       {
379         return false;
380       }
381       if (!f.getName().equals(indexfilename))
382       {
383         return false;
384       }
385     }
386     // should be nothing left in map
387     if (map.size() > 0)
388     {
389       return false;
390     }
391
392     return true;
393   }
394
395   private static boolean checkBackupFiles(int[] indexes) throws IOException
396   {
397     TreeMap<Integer, File> map = BackupFiles
398             .getBackupFilesAsTreeMap(newFile, suffix, digits);
399     for (int m = 0; m < indexes.length; m++)
400     {
401       int i = indexes[m];
402       if (!map.containsKey(i))
403       {
404         return false;
405       }
406       File f = map.get(i);
407       if (!filesContentEqual(newFile, f.getPath()))
408       {
409         return false;
410       }
411       map.remove(i);
412       if (f == null)
413       {
414         return false;
415       }
416       // check the filename -- although this uses the same code to forumulate
417       // the filename so not much of a test!
418       String filename = BackupFilenameParts.getBackupFilename(i,
419               newBasename + testExt, suffix, digits);
420       if (!filename.equals(f.getName()))
421       {
422         System.out.println("Supposed filename '" + filename
423                 + "' not equal to actual filename '" + f.getName() + "'");
424         return false;
425       }
426     }
427     // should be nothing left in map
428     if (map.size() > 0)
429     {
430       return false;
431     }
432
433     return true;
434   }
435
436   private static String[] getBackupFilesAsStrings()
437   {
438     File[] files = getBackupFiles(newFile, suffix, digits);
439     String[] filenames = new String[files.length];
440     for (int i = 0; i < files.length; i++)
441     {
442       filenames[i] = files[i].getPath();
443     }
444     return filenames;
445   }
446
447   public static boolean sequencesEqual(SequenceI s1, SequenceI s2)
448   {
449     if (s1 == null && s2 == null)
450     {
451       return true;
452     }
453     else if (s1 == null || s2 == null)
454     {
455       return false;
456     }
457     return (s1.getName().equals(s2.getName())
458             && s1.getDescription().equals(s2.getDescription())
459             && Arrays.equals(s1.getSequence(), s2.getSequence()));
460   }
461
462   public static boolean filesContentEqual(String fileName1,
463           String fileName2) throws IOException
464   {
465     Path file1 = Paths.get(fileName1);
466     Path file2 = Paths.get(fileName2);
467     byte[] bytes1 = Files.readAllBytes(file1);
468     byte[] bytes2 = Files.readAllBytes(file2);
469     return Arrays.equals(bytes1, bytes2);
470   }
471
472 }