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