JAL-629 Tidy up tests and replaced methods before merge to develop
[jalview.git] / test / jalview / bin / CommandLineOperations.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.bin;
22
23 import static org.testng.Assert.assertNotNull;
24 import static org.testng.Assert.assertTrue;
25
26 import java.io.BufferedReader;
27 import java.io.File;
28 import java.io.IOException;
29 import java.io.InputStreamReader;
30 import java.nio.file.Path;
31 import java.nio.file.Paths;
32 import java.util.ArrayList;
33
34 import org.testng.Assert;
35 import org.testng.FileAssert;
36 import org.testng.annotations.BeforeClass;
37 import org.testng.annotations.DataProvider;
38 import org.testng.annotations.Test;
39
40 import io.github.classgraph.ClassGraph;
41 import io.github.classgraph.ModuleRef;
42 import io.github.classgraph.ScanResult;
43 import jalview.gui.JvOptionPane;
44
45 public class CommandLineOperations
46 {
47
48   @BeforeClass(alwaysRun = true)
49   public void setUpJvOptionPane()
50   {
51     JvOptionPane.setInteractiveMode(false);
52     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
53   }
54
55   // Note longer timeout needed on full test run than on individual tests
56   private static final int TEST_TIMEOUT = 15000;
57
58   private static final int SETUP_TIMEOUT = 9500;
59
60   private static final int MINFILESIZE_SMALL = 2096;
61
62   private static final int MINFILESIZE_BIG = 4096;
63
64   private ArrayList<String> successfulCMDs = new ArrayList<>();
65
66   /***
67    * from
68    * http://stackoverflow.com/questions/808276/how-to-add-a-timeout-value-when
69    * -using-javas-runtime-exec
70    * 
71    * @author jimp
72    * 
73    */
74   private static class Worker extends Thread
75   {
76     private final Process process;
77
78     private BufferedReader outputReader;
79
80     private BufferedReader errorReader;
81
82     private Integer exit;
83
84     private Worker(Process process)
85     {
86       this.process = process;
87     }
88
89     @Override
90     public void run()
91     {
92       try
93       {
94         exit = process.waitFor();
95       } catch (InterruptedException ignore)
96       {
97         return;
98       }
99     }
100
101     public BufferedReader getOutputReader()
102     {
103       return outputReader;
104     }
105
106     public void setOutputReader(BufferedReader outputReader)
107     {
108       this.outputReader = outputReader;
109     }
110
111     public BufferedReader getErrorReader()
112     {
113       return errorReader;
114     }
115
116     public void setErrorReader(BufferedReader errorReader)
117     {
118       this.errorReader = errorReader;
119     }
120   }
121
122   private static ClassGraph scanner = null;
123
124   private static String classpath = null;
125
126   private static String modules = null;
127
128   private static String java_exe = null;
129
130   public synchronized static String getClassPath()
131   {
132     if (scanner == null)
133     {
134       scanner = new ClassGraph();
135       ScanResult scan = scanner.scan();
136       classpath = scan.getClasspath();
137       modules = "";
138       for (ModuleRef mr : scan.getModules())
139       {
140         modules.concat(mr.getName());
141       }
142       java_exe = System.getProperty("java.home") + File.separator + "bin"
143               + File.separator + "java";
144
145     }
146     while (classpath == null)
147     {
148       try
149       {
150         Thread.sleep(10);
151       } catch (InterruptedException x)
152       {
153
154       }
155     }
156     return classpath;
157   }
158
159   private Worker getJalviewDesktopRunner(boolean withAwt, String cmd,
160           int timeout)
161   {
162     // Note: JAL-3065 - don't include quotes for lib/* because the arguments are
163     // not expanded by the shell
164     String classpath = getClassPath();
165     String _cmd = java_exe + " "
166             + (withAwt ? "-Djava.awt.headless=true" : "") + " -classpath "
167             + classpath
168             + (modules.length() > 2 ? "--add-modules=\"" + modules + "\""
169                     : "")
170             + " jalview.bin.Jalview ";
171     Process ls2_proc = null;
172     Worker worker = null;
173     try
174     {
175       ls2_proc = Runtime.getRuntime().exec(_cmd + cmd);
176     } catch (Throwable e1)
177     {
178       e1.printStackTrace();
179     }
180     if (ls2_proc != null)
181     {
182       BufferedReader outputReader = new BufferedReader(
183               new InputStreamReader(ls2_proc.getInputStream()));
184       BufferedReader errorReader = new BufferedReader(
185               new InputStreamReader(ls2_proc.getErrorStream()));
186       worker = new Worker(ls2_proc);
187       worker.start();
188       try
189       {
190         worker.join(timeout);
191       } catch (InterruptedException e)
192       {
193         System.err.println("Thread interrupted");
194       }
195       worker.setOutputReader(outputReader);
196       worker.setErrorReader(errorReader);
197     }
198     return worker;
199   }
200
201   @Test(groups = { "Functional" })
202   public void reportCurrentWorkingDirectory()
203   {
204     try
205     {
206       Path currentRelativePath = Paths.get("");
207       String s = currentRelativePath.toAbsolutePath().toString();
208       System.out.println("Test CWD is " + s);
209     } catch (Exception q)
210     {
211       q.printStackTrace();
212     }
213   }
214
215   @BeforeClass(alwaysRun = true)
216   public void initialize()
217   {
218     new CommandLineOperations();
219   }
220
221   @BeforeClass(alwaysRun = true)
222   public void setUpForHeadlessCommandLineInputOperations()
223           throws IOException
224   {
225     String cmds = "nodisplay -open examples/uniref50.fa -sortbytree -props test/jalview/bin/testProps.jvprops -colour zappo "
226             + "-jabaws http://www.compbio.dundee.ac.uk/jabaws -nosortbytree "
227             + "-features examples/testdata/plantfdx.features -annotations examples/testdata/plantfdx.annotations -tree examples/testdata/uniref50_test_tree";
228     Worker worker = getJalviewDesktopRunner(true, cmds, SETUP_TIMEOUT);
229     String ln = null;
230     while ((ln = worker.getOutputReader().readLine()) != null)
231     {
232       System.out.println(ln);
233       successfulCMDs.add(ln);
234     }
235     while ((ln = worker.getErrorReader().readLine()) != null)
236     {
237       System.err.println(ln);
238     }
239   }
240
241   @BeforeClass(alwaysRun = true)
242   public void setUpForCommandLineInputOperations() throws IOException
243   {
244     String cmds = "-open examples/uniref50.fa -noquestionnaire -nousagestats";
245     final Worker worker = getJalviewDesktopRunner(false, cmds,
246             SETUP_TIMEOUT);
247
248     // number of lines expected on STDERR when Jalview starts up normally
249     // may need to adjust this if Jalview is excessively noisy ?
250     final int STDERR_SETUPLINES = 50;
251
252     // thread monitors stderr - bails after SETUP_TIMEOUT or when
253     // STDERR_SETUPLINES have been read
254     Thread runner = new Thread(new Runnable()
255     {
256       @Override
257       public void run()
258       {
259         String ln = null;
260         int count = 0;
261         try
262         {
263           while ((ln = worker.getOutputReader().readLine()) != null)
264           {
265             System.out.println(ln);
266             successfulCMDs.add(ln);
267             if (++count > STDERR_SETUPLINES)
268             {
269               break;
270             }
271           }
272         } catch (Exception e)
273         {
274           System.err.println(
275                   "Unexpected Exception reading stderr from the Jalview process");
276           e.printStackTrace();
277         }
278       }
279     });
280     long t = System.currentTimeMillis() + SETUP_TIMEOUT;
281     runner.start();
282     while (!runner.isInterrupted() && System.currentTimeMillis() < t)
283     {
284       try
285       {
286         Thread.sleep(500);
287       } catch (InterruptedException e)
288       {
289       }
290     }
291     runner.interrupt();
292     if (worker != null && worker.exit == null)
293     {
294       worker.interrupt();
295       Thread.currentThread().interrupt();
296       worker.process.destroy();
297     }
298   }
299
300   @Test(groups = { "Functional" }, dataProvider = "allInputOperationsData")
301   public void testAllInputOperations(String expectedString,
302           String failureMsg)
303   {
304     Assert.assertTrue(successfulCMDs.contains(expectedString), failureMsg);
305   }
306
307   @Test(
308     groups =
309     { "Functional" },
310     dataProvider = "headlessModeOutputOperationsData")
311   public void testHeadlessModeOutputOperations(String harg, String type,
312           String fileName, boolean withAWT, int expectedMinFileSize,
313           int timeout)
314   {
315     String cmd = harg + type + " " + fileName;
316     // System.out.println(">>>>>>>>>>>>>>>> Command : " + cmd);
317     File file = new File(fileName);
318     file.deleteOnExit();
319     Worker worker = getJalviewDesktopRunner(withAWT, cmd, timeout);
320     assertNotNull(worker, "worker is null");
321     String msg = "Didn't create an output" + type + " file.[" + harg + "]";
322     assertTrue(file.exists(), msg);
323     FileAssert.assertFile(file, msg);
324     FileAssert.assertMinLength(file, expectedMinFileSize);
325     if (worker != null && worker.exit == null)
326     {
327       worker.interrupt();
328       Thread.currentThread().interrupt();
329       worker.process.destroy();
330       Assert.fail("Jalview did not exit after " + type
331               + " generation (try running test again to verify - timeout at "
332               + timeout + "ms). [" + harg + "]");
333     }
334     file.delete();
335   }
336
337   @DataProvider(name = "allInputOperationsData")
338   public Object[][] getHeadlessModeInputParams()
339   {
340     return new Object[][] {
341         // headless mode input operations
342         { "CMD [-colour zappo] executed successfully!",
343             "Failed command : -colour zappo" },
344         { "CMD [-props test/jalview/bin/testProps.jvprops] executed successfully!",
345             "Failed command : -props File" },
346         { "CMD [-sortbytree] executed successfully!",
347             "Failed command : -sortbytree" },
348         { "CMD [-jabaws http://www.compbio.dundee.ac.uk/jabaws] executed successfully!",
349             "Failed command : -jabaws http://www.compbio.dundee.ac.uk/jabaws" },
350         { "CMD [-open examples/uniref50.fa] executed successfully!",
351             "Failed command : -open examples/uniref50.fa" },
352         { "CMD [-nosortbytree] executed successfully!",
353             "Failed command : -nosortbytree" },
354         { "CMD [-features examples/testdata/plantfdx.features]  executed successfully!",
355             "Failed command : -features examples/testdata/plantfdx.features" },
356         { "CMD [-annotations examples/testdata/plantfdx.annotations] executed successfully!",
357             "Failed command : -annotations examples/testdata/plantfdx.annotations" },
358         { "CMD [-tree examples/testdata/uniref50_test_tree] executed successfully!",
359             "Failed command : -tree examples/testdata/uniref50_test_tree" },
360         // non headless mode input operations
361         { "CMD [-nousagestats] executed successfully!",
362             "Failed command : -nousagestats" },
363         { "CMD [-noquestionnaire] executed successfully!",
364             "Failed command : -noquestionnaire" } };
365   }
366
367   @DataProvider(name = "headlessModeOutputOperationsData")
368   public static Object[][] getHeadlessModeOutputParams()
369   {
370     // JBPNote: I'm not clear why need to specify full path for output file
371     // when running tests on build server, but we will keep this patch for now
372     // since it works.
373     // https://issues.jalview.org/browse/JAL-1889?focusedCommentId=21609&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-21609
374     String workingDir = "test/jalview/bin/";
375     return new Object[][] {
376         //
377         { "nodisplay -open examples/uniref50.fa", " -eps",
378             workingDir + "test_uniref50_out.eps", true, MINFILESIZE_BIG,
379             TEST_TIMEOUT },
380         { "nodisplay -open examples/uniref50.fa", " -eps",
381             workingDir + "test_uniref50_out.eps", false, MINFILESIZE_BIG,
382             TEST_TIMEOUT },
383         { "nogui -open examples/uniref50.fa", " -eps",
384             workingDir + "test_uniref50_out.eps", true, MINFILESIZE_BIG,
385             TEST_TIMEOUT },
386         { "nogui -open examples/uniref50.fa", " -eps",
387             workingDir + "test_uniref50_out.eps", false, MINFILESIZE_BIG,
388             TEST_TIMEOUT },
389         { "headless -open examples/uniref50.fa", " -eps",
390             workingDir + "test_uniref50_out.eps", true, MINFILESIZE_BIG,
391             TEST_TIMEOUT },
392         { "headless -open examples/uniref50.fa", " -svg",
393             workingDir + "test_uniref50_out.svg", false, MINFILESIZE_BIG,
394             TEST_TIMEOUT },
395         { "headless -open examples/uniref50.fa", " -png",
396             workingDir + "test_uniref50_out.png", true, MINFILESIZE_BIG,
397             TEST_TIMEOUT },
398         { "headless -open examples/uniref50.fa", " -html",
399             workingDir + "test_uniref50_out.html", true, MINFILESIZE_BIG,
400             TEST_TIMEOUT },
401         { "headless -open examples/uniref50.fa", " -fasta",
402             workingDir + "test_uniref50_out.mfa", true, MINFILESIZE_SMALL,
403             TEST_TIMEOUT },
404         { "headless -open examples/uniref50.fa", " -clustal",
405             workingDir + "test_uniref50_out.aln", true, MINFILESIZE_SMALL,
406             TEST_TIMEOUT },
407         { "headless -open examples/uniref50.fa", " -msf",
408             workingDir + "test_uniref50_out.msf", true, MINFILESIZE_SMALL,
409             TEST_TIMEOUT },
410         { "headless -open examples/uniref50.fa", " -pileup",
411             workingDir + "test_uniref50_out.aln", true, MINFILESIZE_SMALL,
412             TEST_TIMEOUT },
413         { "headless -open examples/uniref50.fa", " -pir",
414             workingDir + "test_uniref50_out.pir", true, MINFILESIZE_SMALL,
415             TEST_TIMEOUT },
416         { "headless -open examples/uniref50.fa", " -pfam",
417             workingDir + "test_uniref50_out.pfam", true, MINFILESIZE_SMALL,
418             TEST_TIMEOUT },
419         { "headless -open examples/uniref50.fa", " -blc",
420             workingDir + "test_uniref50_out.blc", true, MINFILESIZE_SMALL,
421             TEST_TIMEOUT },
422         { "headless -open examples/uniref50.fa", " -jalview",
423             workingDir + "test_uniref50_out.jvp", true, MINFILESIZE_SMALL,
424             TEST_TIMEOUT },
425         //
426     };
427   }
428 }