View Javadoc

1   /*
2    * This file is part of hyphenType. hyphenType is free software: you can
3    * redistribute it and/or modify it under the terms of the GNU General Public
4    * License as published by the Free Software Foundation, either version 3 of the
5    * License, or (at your option) any later version. hyphenType is distributed in
6    * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
7    * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
8    * the GNU General Public License for more details. You should have received a
9    * copy of the GNU General Public License along with hyphenType. If not, see
10   * <http://www.gnu.org/licenses/>.
11   */
12  package org.hyphenType.unittesting;
13  
14  import java.io.ByteArrayOutputStream;
15  import java.io.File;
16  import java.io.FileNotFoundException;
17  import java.io.FileOutputStream;
18  import java.io.IOException;
19  import java.io.PrintStream;
20  
21  import org.hyphenType.datastructure.Options;
22  import org.hyphenType.exceptions.InvalidOptionsInterfaceException;
23  import org.hyphenType.lexerparser.exceptions.MandatoryMapValueNotFoundException;
24  import org.hyphenType.lexerparser.exceptions.MandatorySimpleArgumentNotFoundException;
25  import org.hyphenType.lexerparser.exceptions.MandatoryValueNotFoundException;
26  import org.hyphenType.lexerparser.exceptions.RegexMismatchException;
27  import org.hyphenType.lexerparser.exceptions.StringParseException;
28  import org.hyphenType.lexerparser.exceptions.StringParsingErrorException;
29  import org.hyphenType.optionsextractor.OptionsExtractorException;
30  import org.hyphenType.wrapper.StandAloneAppWrapper;
31  
32  /**
33   * @author Aurelio Akira M. Matsui
34   */
35  public final class UnitTestingAppEngine {
36  
37      /**
38       * TODO write.
39       */
40      private Class<?> clazz;
41      /**
42       * TODO write.
43       */
44      private String[] args;
45  
46      /**
47       * TODO write.
48       */
49      private final String[] errs;
50      /**
51       * TODO write.
52       */
53      private final String[] outs;
54  
55      /**
56       * TODO write.
57       */
58      private int errsIndex = -1;
59      /**
60       * TODO write.
61       */
62      private int outsIndex = -1;
63  
64      /**
65       * TODO write.
66       */
67      private NonExceptionalExit nonExceptionalExit = null;
68  
69      /**
70       * TODO write.
71       */
72      private Throwable throwable = null;
73  
74      /**
75       * @param appClass
76       * @param arguments
77       * @throws InvalidOptionsInterfaceException
78       * @throws InvalidOptionException
79       */
80      public UnitTestingAppEngine(final Class<?> appClass, final String... arguments) throws InvalidOptionsInterfaceException {
81          this(appClass, new UnitTestingUserInput(), arguments);
82      }
83  
84      /**
85       * @param appClass
86       * @param input
87       * @param arguments
88       * @throws InvalidOptionsInterfaceException
89       * @throws InvalidOptionException
90       */
91      public UnitTestingAppEngine(final Class<?> appClass, final UnitTestingUserInput input, final String... arguments) throws InvalidOptionsInterfaceException {
92  
93          clazz = appClass;
94          args = arguments;
95  
96          PrintStream errBkp = System.err;
97          ByteArrayOutputStream errBaos = new ByteArrayOutputStream();
98          PrintStream err = new PrintStream(errBaos);
99          System.setErr(err);
100 
101         PrintStream outBkp = System.out;
102         ByteArrayOutputStream outBaos = new ByteArrayOutputStream();
103         PrintStream out = new PrintStream(outBaos);
104         System.setOut(out);
105 
106         UnitTestingAppWrapper appWrapper = new UnitTestingAppWrapper(input);
107         try {
108             appWrapper.invokeMain(appClass, arguments, false);
109         } catch (NonExceptionalExit e) {
110             System.out.println("!!!!");
111             this.nonExceptionalExit = e;
112         } catch (Throwable e) {
113             throwable = e;
114         }
115 
116         err.flush();
117         out.flush();
118         errs = errBaos.toString().split("\n");
119         outs = outBaos.toString().split("\n");
120 
121         System.setErr(errBkp);
122         System.setOut(outBkp);
123     }
124 
125     /**
126      * @return
127      */
128     public String err() {
129         if (errsIndex + 1 < errs.length) {
130             errsIndex++;
131             return errs[errsIndex];
132         }
133         return null;
134     }
135 
136     /**
137      * @return
138      */
139     public String out() {
140         if (outsIndex + 1 < outs.length) {
141             outsIndex++;
142             return outs[outsIndex];
143         }
144         return null;
145     }
146 
147     /**
148      * @return
149      */
150     public Enum<?> getStatusCodeEnum() {
151         return nonExceptionalExit.getStatusCodeEnum();
152     }
153 
154     /**
155      * @return True means something called {@link Options#exit(Enum)}.
156      */
157     public boolean exitEnumCalled() {
158         return nonExceptionalExit != null && nonExceptionalExit.exitEnumCalled();
159     }
160 
161     /**
162      * @return
163      */
164     public int getStatusCodeInt() {
165         return nonExceptionalExit.getStatusCodeInt();
166     }
167 
168     /**
169      * @return True means something called {@link Options#exit(int)}.
170      */
171     public boolean exitIntCalled() {
172         return nonExceptionalExit != null && nonExceptionalExit.exitIntCalled();
173     }
174 
175     /**
176      * Retrieves at which point the application under test called
177      * {@link Options#exit(Enum)} or {@link Options#exit(int)}.
178      * 
179      * @return The stack trace element that called {@link Options#exit(Enum)} or
180      *         {@link Options#exit(int)}.
181      */
182     public StackTraceElement exitCallPoint() {
183         return nonExceptionalExit.exitCallPoint();
184     }
185 
186     /**
187      * Writes the output written to stdout to a file.
188      * 
189      * @param fileName
190      *            The file name to save output to.
191      */
192     public void saveStdoutInteraction(final String fileName) {
193         try {
194             FileOutputStream fos = new FileOutputStream(new File(fileName));
195             fos.write(("_> java " + clazz.getName()).getBytes());
196             for (String arg : args) {
197                 fos.write((" " + arg).getBytes());
198             }
199             fos.write("\n".getBytes());
200             for (String out : outs) {
201                 fos.write((out + "\n").getBytes());
202             }
203             fos.flush();
204             fos.close();
205         } catch (FileNotFoundException e) {
206             // TODO Auto-generated catch block
207             e.printStackTrace();
208         } catch (IOException e) {
209             // TODO Auto-generated catch block
210             e.printStackTrace();
211         }
212     }
213 
214     /**
215      * Writes the output written to stdout to a file using HTML snippet format.
216      * 
217      * @param fileName
218      *            The file name to save output to.
219      */
220     public void saveStdoutInteractionHTML(final String fileName) {
221         saveStdoutInteractionHTML(fileName, "", "");
222     }
223     
224     /**
225      * Writes the output written to stdout to a file using HTML snippet format.
226      * 
227      * @param fileName
228      *            The file name to save output to.
229      * @param prefix
230      *            The string to add at the beginning of the generated HTML
231      * @param sufix
232      *            The string to add at the end of the generated HTML
233      */
234     public void saveStdoutInteractionHTML(final String fileName, final String prefix, final String sufix) {
235         try {
236             FileOutputStream fos = new FileOutputStream(new File(fileName));
237             fos.write(prefix.getBytes());
238             fos.write(("<strong>_> java " + clazz.getName()).getBytes());
239             for (String arg : args) {
240                 fos.write((" " + arg).getBytes());
241             }
242             fos.write("</strong>\n".getBytes());
243             for (String out : outs) {
244                 fos.write(out.getBytes());
245                 fos.write("\n".getBytes());
246             }
247             fos.write(sufix.getBytes());
248             fos.flush();
249             fos.close();
250         } catch (FileNotFoundException e) {
251             // TODO Auto-generated catch block
252             e.printStackTrace();
253         } catch (IOException e) {
254             // TODO Auto-generated catch block
255             e.printStackTrace();
256         }
257     }
258 
259     /**
260      * If the execution of the application under test threw any
261      * {@link Throwable} that was not caught by any exit status constant,
262      * this method will return this {@link Throwable}.
263      * 
264      * @return The {@link Throwable} that was possibly thrown during the
265      *         execution of the application. Returns null if no throwable
266      *         was thrown and uncaught.
267      */
268     public Throwable uncaughtThrowable() {
269         return throwable;
270     }
271 
272     /**
273      * An application wrapper especially designed for unit testing. This
274      * application wrapper prevents calls to {@link Options#exit(Enum)} or
275      * {@link Options#exit(int)} to actually exit the JVM. Instead, the
276      * replacement will simply log these calls.
277      * 
278      * @author akira
279      * @see UnitTestingOptionExtractor
280      */
281     private class UnitTestingAppWrapper extends StandAloneAppWrapper {
282 
283         /**
284          * The emulated user input.
285          */
286         private UnitTestingUserInput input;
287 
288         /**
289          * @param input
290          *            The mock user input to emulate user inputs when some
291          *            argument was not passed in command line.
292          */
293         UnitTestingAppWrapper(final UnitTestingUserInput input) {
294             this.input = input;
295         }
296 
297         @SuppressWarnings("unchecked")
298         @Override
299         protected Options<?> buildOptionsObject(final Class<?> clazz, final String[] arguments) throws InvalidOptionsInterfaceException, StringParsingErrorException, MandatoryValueNotFoundException, RegexMismatchException, StringParseException, MandatoryMapValueNotFoundException, MandatorySimpleArgumentNotFoundException, OptionsExtractorException {
300             UnitTestingOptionExtractor<?> optionsExtractor = new UnitTestingOptionExtractor(clazz);
301             return optionsExtractor.options(input, arguments);
302         }
303     }
304 }