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