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