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 }