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