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