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 org.hyphenType.datastructure.Options;
15  import org.hyphenType.dynamicproxy.AbstractArgumentsInvocationHandler;
16  import org.hyphenType.dynamicproxy.ConcreteArgumentsInvocationHandler;
17  import org.hyphenType.exit.StatusCode;
18  import org.hyphenType.lexerparser.OptionValues;
19  
20  /**
21   * A mock to replace {@link ConcreteArgumentsInvocationHandler}. When this class is used
22   * instead of {@link ConcreteArgumentsInvocationHandler}, this class avoids calls to
23   * {@link ArgumentsInvocationHandler#exit(Enum))} and
24   * {@link ArgumentsInvocationHandler#exit(int)))} to actually terminate the JVM.
25   * Instead, this class will log those calls. This way, a unit test can read the
26   * logs and determine if user source code is trying to terminate the JVM
27   * correctly.<br/>
28   * <br/>
29   * This class was designed to be utilized by {@link UnitTestingOptionExtractor}.
30   * 
31   * @author Aurelio Akira M. Matsui
32   * @see UnitTestingOptionExtractor
33   * @see ConcreteArgumentsInvocationHandler
34   * @param <T>
35   *            The option interface type.
36   */
37  public class MockArgumentsInvocationHandler<T extends Options<?>> extends AbstractArgumentsInvocationHandler<T> {
38  
39      /**
40       * Whether one called the {@link MockArgumentsInvocationHandler#exit(Enum)} method or not.
41       */
42      private boolean exitEnumCalled = false;
43      /**
44       * The exit status code (enumeration), if called.
45       */
46      private Enum<?> statusCodeEnum = null;
47  
48      /**
49       * Whether one called the {@link MockArgumentsInvocationHandler#exit(int)} method or not.
50       */
51      private boolean exitIntCalled = false;
52      /**
53       * The exit status code (int), if called.
54       */
55      private int statusCodeInt = 0;
56  
57      /**
58       * Whether this object will throw the {@link NonExceptionalExit} exception or not.
59       * 
60       * @see NonExceptionalExit
61       */
62      private final boolean throwsNonExceptionalExit;
63  
64      /**
65       * Sole constructor.
66       * 
67       * @param values
68       *            The values parsed.
69       * @param formatter
70       *            The documentation formatter.
71       * @param exitCodeEnumClass
72       *            The enumeration class that will be used to exit.
73       * @param rawArguments
74       *            The raw arguments "received" from the command line.
75       * @param throwsNonExceptionalExit
76       *            Whether this mock implementation will throw the
77       *            {@link NonExceptionalExit} exception or not.
78       */
79      public MockArgumentsInvocationHandler(final OptionValues<T> values, final Class<? extends Options<?>> optionsInterface, final Class<? extends StatusCode> exitCodeEnumClass, final String[] rawArguments, final boolean throwsNonExceptionalExit) {
80          super(values, optionsInterface, exitCodeEnumClass, rawArguments);
81          this.throwsNonExceptionalExit = throwsNonExceptionalExit;
82      }
83  
84      @Override
85      protected final void exit(final Enum<?> e) {
86          if (exitEnumCalled) {
87              throw new RuntimeException("Invalid state. Cannot call exit(Enum) more than once.");
88          }
89          exitEnumCalled = true;
90          System.out.println("Pretending to exit the VM using code: " + e);
91          statusCodeEnum = e;
92          if (throwsNonExceptionalExit) {
93              throw new NonExceptionalExit(e);
94          }
95      }
96  
97      @Override
98      protected final void exit(final int code) {
99          if (exitIntCalled) {
100             throw new RuntimeException("Invalid state. Cannot call exit(int) more than once.");
101         }
102         exitIntCalled = true;
103         System.out.println("Pretending to exit the VM using code: " + code);
104         statusCodeInt = code;
105         if (throwsNonExceptionalExit) {
106             throw new NonExceptionalExit(code);
107         }
108     }
109 
110     /**
111      * Useful to analyze how the
112      * {@link MockArgumentsInvocationHandler#exit(Enum)} method was called.
113      * 
114      * @return The status code returned by the last call to the
115      *         {@link MockArgumentsInvocationHandler#exit(Enum)} method.
116      */
117     public final Enum<?> getStatusCodeEnum() {
118         if (!exitEnumCalled) {
119             throw new RuntimeException("Invalid state. exit(Enum) was not called yet.");
120         }
121         return statusCodeEnum;
122     }
123 
124     /**
125      * Useful to check whether the
126      * {@link MockArgumentsInvocationHandler#exit(Enum)} method was called or
127      * not.
128      * 
129      * @return Whether the method
130      *         {@link MockArgumentsInvocationHandler#exit(Enum)} was called or
131      *         not.
132      */
133     public final boolean exitEnumCalled() {
134         return exitEnumCalled;
135     }
136 
137     /**
138      * Useful to analyze how the
139      * {@link MockArgumentsInvocationHandler#exit(int)} method was called.
140      * 
141      * @return The status code returned by the last call to the
142      *         {@link MockArgumentsInvocationHandler#exit(int)} method.
143      */
144     public final int getStatusCodeInt() {
145         if (!exitIntCalled) {
146             throw new RuntimeException("Invalid state. exit(int) was not called yet.");
147         }
148         return statusCodeInt;
149     }
150 
151     /**
152      * Useful to check whether the
153      * {@link MockArgumentsInvocationHandler#exit(int)} method was called or
154      * not.
155      * 
156      * @return Whether the method
157      *         {@link MockArgumentsInvocationHandler#exit(int)} was called or
158      *         not.
159      */
160     public final boolean exitIntCalled() {
161         return exitIntCalled;
162     }
163 }