1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.squareup.okhttp.internal; 19 20 import org.junit.Test; 21 22 import java.io.IOException; 23 import java.lang.reflect.InvocationTargetException; 24 25 import static org.junit.Assert.assertEquals; 26 import static org.junit.Assert.assertFalse; 27 import static org.junit.Assert.assertNull; 28 import static org.junit.Assert.assertTrue; 29 import static org.junit.Assert.fail; 30 31 /** 32 * Tests for {@link OptionalMethod}. 33 */ 34 public class OptionalMethodTest { 35 @SuppressWarnings("unused") 36 private static class BaseClass { 37 public String stringMethod() { 38 return "string"; 39 } 40 41 public void voidMethod() {} 42 } 43 44 @SuppressWarnings("unused") 45 private static class SubClass1 extends BaseClass { 46 public String subclassMethod() { 47 return "subclassMethod1"; 48 } 49 50 public String methodWithArgs(String arg) { 51 return arg; 52 } 53 } 54 55 @SuppressWarnings("unused") 56 private static class SubClass2 extends BaseClass { 57 public int subclassMethod() { 58 return 1234; 59 } 60 61 public String methodWithArgs(String arg) { 62 return arg; 63 } 64 65 public void throwsException() throws IOException { 66 throw new IOException(); 67 } 68 69 public void throwsRuntimeException() throws Exception { 70 throw new NumberFormatException(); 71 } 72 73 protected void nonPublic() {} 74 } 75 76 private final static OptionalMethod<BaseClass> STRING_METHOD_RETURNS_ANY = 77 new OptionalMethod<BaseClass>(null, "stringMethod"); 78 private final static OptionalMethod<BaseClass> STRING_METHOD_RETURNS_STRING = 79 new OptionalMethod<BaseClass>(String.class, "stringMethod"); 80 private final static OptionalMethod<BaseClass> STRING_METHOD_RETURNS_INT = 81 new OptionalMethod<BaseClass>(Integer.TYPE, "stringMethod"); 82 private final static OptionalMethod<BaseClass> VOID_METHOD_RETURNS_ANY = 83 new OptionalMethod<BaseClass>(null, "voidMethod"); 84 private final static OptionalMethod<BaseClass> VOID_METHOD_RETURNS_VOID = 85 new OptionalMethod<BaseClass>(Void.TYPE, "voidMethod"); 86 private final static OptionalMethod<BaseClass> SUBCLASS_METHOD_RETURNS_ANY = 87 new OptionalMethod<BaseClass>(null, "subclassMethod"); 88 private final static OptionalMethod<BaseClass> SUBCLASS_METHOD_RETURNS_STRING = 89 new OptionalMethod<BaseClass>(String.class, "subclassMethod"); 90 private final static OptionalMethod<BaseClass> SUBCLASS_METHOD_RETURNS_INT = 91 new OptionalMethod<BaseClass>(Integer.TYPE, "subclassMethod"); 92 private final static OptionalMethod<BaseClass> METHOD_WITH_ARGS_WRONG_PARAMS = 93 new OptionalMethod<BaseClass>(null, "methodWithArgs", Integer.class); 94 private final static OptionalMethod<BaseClass> METHOD_WITH_ARGS_CORRECT_PARAMS = 95 new OptionalMethod<BaseClass>(null, "methodWithArgs", String.class); 96 97 private final static OptionalMethod<BaseClass> THROWS_EXCEPTION = 98 new OptionalMethod<BaseClass>(null, "throwsException"); 99 private final static OptionalMethod<BaseClass> THROWS_RUNTIME_EXCEPTION = 100 new OptionalMethod<BaseClass>(null, "throwsRuntimeException"); 101 private final static OptionalMethod<BaseClass> NON_PUBLIC = 102 new OptionalMethod<BaseClass>(null, "nonPublic"); 103 104 @Test 105 public void isSupported() throws Exception { 106 { 107 BaseClass base = new BaseClass(); 108 assertTrue(STRING_METHOD_RETURNS_ANY.isSupported(base)); 109 assertTrue(STRING_METHOD_RETURNS_STRING.isSupported(base)); 110 assertFalse(STRING_METHOD_RETURNS_INT.isSupported(base)); 111 assertTrue(VOID_METHOD_RETURNS_ANY.isSupported(base)); 112 assertTrue(VOID_METHOD_RETURNS_VOID.isSupported(base)); 113 assertFalse(SUBCLASS_METHOD_RETURNS_ANY.isSupported(base)); 114 assertFalse(SUBCLASS_METHOD_RETURNS_STRING.isSupported(base)); 115 assertFalse(SUBCLASS_METHOD_RETURNS_INT.isSupported(base)); 116 assertFalse(METHOD_WITH_ARGS_WRONG_PARAMS.isSupported(base)); 117 assertFalse(METHOD_WITH_ARGS_CORRECT_PARAMS.isSupported(base)); 118 } 119 { 120 SubClass1 subClass1 = new SubClass1(); 121 assertTrue(STRING_METHOD_RETURNS_ANY.isSupported(subClass1)); 122 assertTrue(STRING_METHOD_RETURNS_STRING.isSupported(subClass1)); 123 assertFalse(STRING_METHOD_RETURNS_INT.isSupported(subClass1)); 124 assertTrue(VOID_METHOD_RETURNS_ANY.isSupported(subClass1)); 125 assertTrue(VOID_METHOD_RETURNS_VOID.isSupported(subClass1)); 126 assertTrue(SUBCLASS_METHOD_RETURNS_ANY.isSupported(subClass1)); 127 assertTrue(SUBCLASS_METHOD_RETURNS_STRING.isSupported(subClass1)); 128 assertFalse(SUBCLASS_METHOD_RETURNS_INT.isSupported(subClass1)); 129 assertFalse(METHOD_WITH_ARGS_WRONG_PARAMS.isSupported(subClass1)); 130 assertTrue(METHOD_WITH_ARGS_CORRECT_PARAMS.isSupported(subClass1)); 131 } 132 { 133 SubClass2 subClass2 = new SubClass2(); 134 assertTrue(STRING_METHOD_RETURNS_ANY.isSupported(subClass2)); 135 assertTrue(STRING_METHOD_RETURNS_STRING.isSupported(subClass2)); 136 assertFalse(STRING_METHOD_RETURNS_INT.isSupported(subClass2)); 137 assertTrue(VOID_METHOD_RETURNS_ANY.isSupported(subClass2)); 138 assertTrue(VOID_METHOD_RETURNS_VOID.isSupported(subClass2)); 139 assertTrue(SUBCLASS_METHOD_RETURNS_ANY.isSupported(subClass2)); 140 assertFalse(SUBCLASS_METHOD_RETURNS_STRING.isSupported(subClass2)); 141 assertTrue(SUBCLASS_METHOD_RETURNS_INT.isSupported(subClass2)); 142 assertFalse(METHOD_WITH_ARGS_WRONG_PARAMS.isSupported(subClass2)); 143 assertTrue(METHOD_WITH_ARGS_CORRECT_PARAMS.isSupported(subClass2)); 144 } 145 } 146 147 @Test 148 public void invoke() throws Exception { 149 { 150 BaseClass base = new BaseClass(); 151 assertEquals("string", STRING_METHOD_RETURNS_STRING.invoke(base)); 152 assertEquals("string", STRING_METHOD_RETURNS_ANY.invoke(base)); 153 assertErrorOnInvoke(STRING_METHOD_RETURNS_INT, base); 154 assertNull(VOID_METHOD_RETURNS_ANY.invoke(base)); 155 assertNull(VOID_METHOD_RETURNS_VOID.invoke(base)); 156 assertErrorOnInvoke(SUBCLASS_METHOD_RETURNS_ANY, base); 157 assertErrorOnInvoke(SUBCLASS_METHOD_RETURNS_STRING, base); 158 assertErrorOnInvoke(SUBCLASS_METHOD_RETURNS_INT, base); 159 assertErrorOnInvoke(METHOD_WITH_ARGS_WRONG_PARAMS, base); 160 assertErrorOnInvoke(METHOD_WITH_ARGS_CORRECT_PARAMS, base); 161 } 162 { 163 SubClass1 subClass1 = new SubClass1(); 164 assertEquals("string", STRING_METHOD_RETURNS_STRING.invoke(subClass1)); 165 assertEquals("string", STRING_METHOD_RETURNS_ANY.invoke(subClass1)); 166 assertErrorOnInvoke(STRING_METHOD_RETURNS_INT, subClass1); 167 assertNull(VOID_METHOD_RETURNS_ANY.invoke(subClass1)); 168 assertNull(VOID_METHOD_RETURNS_VOID.invoke(subClass1)); 169 assertEquals("subclassMethod1", SUBCLASS_METHOD_RETURNS_ANY.invoke(subClass1)); 170 assertEquals("subclassMethod1", SUBCLASS_METHOD_RETURNS_STRING.invoke(subClass1)); 171 assertErrorOnInvoke(SUBCLASS_METHOD_RETURNS_INT, subClass1); 172 assertErrorOnInvoke(METHOD_WITH_ARGS_WRONG_PARAMS, subClass1); 173 assertEquals("arg", METHOD_WITH_ARGS_CORRECT_PARAMS.invoke(subClass1, "arg")); 174 } 175 176 { 177 SubClass2 subClass2 = new SubClass2(); 178 assertEquals("string", STRING_METHOD_RETURNS_STRING.invoke(subClass2)); 179 assertEquals("string", STRING_METHOD_RETURNS_ANY.invoke(subClass2)); 180 assertErrorOnInvoke(STRING_METHOD_RETURNS_INT, subClass2); 181 assertNull(VOID_METHOD_RETURNS_ANY.invoke(subClass2)); 182 assertNull(VOID_METHOD_RETURNS_VOID.invoke(subClass2)); 183 assertEquals(1234, SUBCLASS_METHOD_RETURNS_ANY.invoke(subClass2)); 184 assertErrorOnInvoke(SUBCLASS_METHOD_RETURNS_STRING, subClass2); 185 assertEquals(1234, SUBCLASS_METHOD_RETURNS_INT.invoke(subClass2)); 186 assertErrorOnInvoke(METHOD_WITH_ARGS_WRONG_PARAMS, subClass2); 187 assertEquals("arg", METHOD_WITH_ARGS_CORRECT_PARAMS.invoke(subClass2, "arg")); 188 } 189 } 190 191 @Test 192 public void invokeBadArgs() throws Exception { 193 SubClass1 subClass1 = new SubClass1(); 194 assertIllegalArgumentExceptionOnInvoke(METHOD_WITH_ARGS_CORRECT_PARAMS, subClass1); // no args 195 assertIllegalArgumentExceptionOnInvoke(METHOD_WITH_ARGS_CORRECT_PARAMS, subClass1, 123); 196 assertIllegalArgumentExceptionOnInvoke(METHOD_WITH_ARGS_CORRECT_PARAMS, subClass1, true); 197 assertIllegalArgumentExceptionOnInvoke(METHOD_WITH_ARGS_CORRECT_PARAMS, subClass1, new Object()); 198 assertIllegalArgumentExceptionOnInvoke(METHOD_WITH_ARGS_CORRECT_PARAMS, subClass1, "one", "two"); 199 } 200 201 @Test 202 public void invokeWithException() throws Exception { 203 SubClass2 subClass2 = new SubClass2(); 204 try { 205 THROWS_EXCEPTION.invoke(subClass2); 206 } catch (InvocationTargetException expected) { 207 assertTrue(expected.getTargetException() instanceof IOException); 208 } 209 210 try { 211 THROWS_RUNTIME_EXCEPTION.invoke(subClass2); 212 } catch (InvocationTargetException expected) { 213 assertTrue(expected.getTargetException() instanceof NumberFormatException); 214 } 215 } 216 217 @Test 218 public void invokeNonPublic() throws Exception { 219 SubClass2 subClass2 = new SubClass2(); 220 assertFalse(NON_PUBLIC.isSupported(subClass2)); 221 assertErrorOnInvoke(NON_PUBLIC, subClass2); 222 } 223 224 @Test 225 public void invokeOptional() throws Exception { 226 { 227 BaseClass base = new BaseClass(); 228 assertEquals("string", STRING_METHOD_RETURNS_STRING.invokeOptional(base)); 229 assertEquals("string", STRING_METHOD_RETURNS_ANY.invokeOptional(base)); 230 assertNull(STRING_METHOD_RETURNS_INT.invokeOptional(base)); 231 assertNull(VOID_METHOD_RETURNS_ANY.invokeOptional(base)); 232 assertNull(VOID_METHOD_RETURNS_VOID.invokeOptional(base)); 233 assertNull(SUBCLASS_METHOD_RETURNS_ANY.invokeOptional(base)); 234 assertNull(SUBCLASS_METHOD_RETURNS_STRING.invokeOptional(base)); 235 assertNull(SUBCLASS_METHOD_RETURNS_INT.invokeOptional(base)); 236 assertNull(METHOD_WITH_ARGS_WRONG_PARAMS.invokeOptional(base)); 237 assertNull(METHOD_WITH_ARGS_CORRECT_PARAMS.invokeOptional(base)); 238 } 239 { 240 SubClass1 subClass1 = new SubClass1(); 241 assertEquals("string", STRING_METHOD_RETURNS_STRING.invokeOptional(subClass1)); 242 assertEquals("string", STRING_METHOD_RETURNS_ANY.invokeOptional(subClass1)); 243 assertNull(STRING_METHOD_RETURNS_INT.invokeOptional(subClass1)); 244 assertNull(VOID_METHOD_RETURNS_ANY.invokeOptional(subClass1)); 245 assertNull(VOID_METHOD_RETURNS_VOID.invokeOptional(subClass1)); 246 assertEquals("subclassMethod1", SUBCLASS_METHOD_RETURNS_ANY.invokeOptional(subClass1)); 247 assertEquals("subclassMethod1", SUBCLASS_METHOD_RETURNS_STRING.invokeOptional(subClass1)); 248 assertNull(SUBCLASS_METHOD_RETURNS_INT.invokeOptional(subClass1)); 249 assertNull(METHOD_WITH_ARGS_WRONG_PARAMS.invokeOptional(subClass1)); 250 assertEquals("arg", METHOD_WITH_ARGS_CORRECT_PARAMS.invokeOptional(subClass1, "arg")); 251 } 252 253 { 254 SubClass2 subClass2 = new SubClass2(); 255 assertEquals("string", STRING_METHOD_RETURNS_STRING.invokeOptional(subClass2)); 256 assertEquals("string", STRING_METHOD_RETURNS_ANY.invokeOptional(subClass2)); 257 assertNull(STRING_METHOD_RETURNS_INT.invokeOptional(subClass2)); 258 assertNull(VOID_METHOD_RETURNS_ANY.invokeOptional(subClass2)); 259 assertNull(VOID_METHOD_RETURNS_VOID.invokeOptional(subClass2)); 260 assertEquals(1234, SUBCLASS_METHOD_RETURNS_ANY.invokeOptional(subClass2)); 261 assertNull(SUBCLASS_METHOD_RETURNS_STRING.invokeOptional(subClass2)); 262 assertEquals(1234, SUBCLASS_METHOD_RETURNS_INT.invokeOptional(subClass2)); 263 assertNull(METHOD_WITH_ARGS_WRONG_PARAMS.invokeOptional(subClass2)); 264 assertEquals("arg", METHOD_WITH_ARGS_CORRECT_PARAMS.invokeOptional(subClass2, "arg")); 265 } 266 } 267 268 @Test 269 public void invokeOptionalBadArgs() throws Exception { 270 SubClass1 subClass1 = new SubClass1(); 271 assertIllegalArgumentExceptionOnInvokeOptional(METHOD_WITH_ARGS_CORRECT_PARAMS, subClass1); // no args 272 assertIllegalArgumentExceptionOnInvokeOptional(METHOD_WITH_ARGS_CORRECT_PARAMS, subClass1, 123); 273 assertIllegalArgumentExceptionOnInvokeOptional(METHOD_WITH_ARGS_CORRECT_PARAMS, subClass1, true); 274 assertIllegalArgumentExceptionOnInvokeOptional(METHOD_WITH_ARGS_CORRECT_PARAMS, subClass1, new Object()); 275 assertIllegalArgumentExceptionOnInvokeOptional(METHOD_WITH_ARGS_CORRECT_PARAMS, subClass1, "one", "two"); 276 } 277 278 @Test 279 public void invokeOptionalWithException() throws Exception { 280 SubClass2 subClass2 = new SubClass2(); 281 try { 282 THROWS_EXCEPTION.invokeOptional(subClass2); 283 } catch (InvocationTargetException expected) { 284 assertTrue(expected.getTargetException() instanceof IOException); 285 } 286 287 try { 288 THROWS_RUNTIME_EXCEPTION.invokeOptional(subClass2); 289 } catch (InvocationTargetException expected) { 290 assertTrue(expected.getTargetException() instanceof NumberFormatException); 291 } 292 } 293 294 @Test 295 public void invokeOptionalNonPublic() throws Exception { 296 SubClass2 subClass2 = new SubClass2(); 297 assertFalse(NON_PUBLIC.isSupported(subClass2)); 298 assertErrorOnInvokeOptional(NON_PUBLIC, subClass2); 299 } 300 301 private static <T> void assertErrorOnInvoke( 302 OptionalMethod<T> optionalMethod, T base, Object... args) throws Exception { 303 try { 304 optionalMethod.invoke(base, args); 305 fail(); 306 } catch (Error expected) { 307 } 308 } 309 310 private static <T> void assertIllegalArgumentExceptionOnInvoke( 311 OptionalMethod<T> optionalMethod, T base, Object... args) throws Exception { 312 try { 313 optionalMethod.invoke(base, args); 314 fail(); 315 } catch (IllegalArgumentException expected) { 316 } 317 } 318 319 private static <T> void assertErrorOnInvokeOptional( 320 OptionalMethod<T> optionalMethod, T base, Object... args) throws Exception { 321 try { 322 optionalMethod.invokeOptional(base, args); 323 fail(); 324 } catch (Error expected) { 325 } 326 } 327 328 private static <T> void assertIllegalArgumentExceptionOnInvokeOptional( 329 OptionalMethod<T> optionalMethod, T base, Object... args) throws Exception { 330 try { 331 optionalMethod.invokeOptional(base, args); 332 fail(); 333 } catch (IllegalArgumentException expected) { 334 } 335 } 336 337 } 338