• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc;
18 
19 import static junit.framework.TestCase.assertFalse;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertTrue;
22 import static org.mockito.Mockito.mockingDetails;
23 import static org.mockito.Mockito.verify;
24 
25 import com.google.common.base.Defaults;
26 import com.google.common.base.MoreObjects;
27 import java.lang.reflect.InvocationTargetException;
28 import java.lang.reflect.Method;
29 import java.lang.reflect.Modifier;
30 import java.util.Collection;
31 import javax.annotation.Nullable;
32 
33 /**
34  * A util class to help test forwarding classes.
35  */
36 public final class ForwardingTestUtil {
37   /**
38    * Use reflection to perform a basic sanity test. The forwarding class should forward all public
39    * methods to the delegate, except for those in skippedMethods.  This does NOT verify that
40    * arguments or return values are forwarded properly.
41    *
42    * @param delegateClass The class whose methods should be forwarded.
43    * @param mockDelegate The mockito mock of the delegate class.
44    * @param forwarder The forwarder object that forwards to the mockDelegate.
45    * @param skippedMethods A collection of methods that are skipped by the test.
46    */
testMethodsForwarded( Class<T> delegateClass, T mockDelegate, T forwarder, Collection<Method> skippedMethods)47   public static <T> void testMethodsForwarded(
48       Class<T> delegateClass,
49       T mockDelegate,
50       T forwarder,
51       Collection<Method> skippedMethods) throws Exception {
52     testMethodsForwarded(
53         delegateClass, mockDelegate, forwarder, skippedMethods,
54         new ArgumentProvider() {
55           @Override
56           public Object get(Method method, int argPos, Class<?> clazz) {
57             return null;
58           }
59         });
60   }
61 
62   /**
63    * Use reflection to perform a basic sanity test. The forwarding class should forward all public
64    * methods to the delegate, except for those in skippedMethods.  This does NOT verify that return
65    * values are forwarded properly, and can only verify the propagation of arguments for which
66    * {@code argProvider} returns distinctive non-null values.
67    *
68    * @param delegateClass The class whose methods should be forwarded.
69    * @param mockDelegate The mockito mock of the delegate class.
70    * @param forwarder The forwarder object that forwards to the mockDelegate.
71    * @param skippedMethods A collection of methods that are skipped by the test.
72    * @param argProvider provides argument to be passed to tested forwarding methods.
73    */
testMethodsForwarded( Class<T> delegateClass, T mockDelegate, T forwarder, Collection<Method> skippedMethods, ArgumentProvider argProvider)74   public static <T> void testMethodsForwarded(
75       Class<T> delegateClass,
76       T mockDelegate,
77       T forwarder,
78       Collection<Method> skippedMethods,
79       ArgumentProvider argProvider) throws Exception {
80     assertTrue(mockingDetails(mockDelegate).isMock());
81     assertFalse(mockingDetails(forwarder).isMock());
82 
83     for (Method method : delegateClass.getDeclaredMethods()) {
84       if (Modifier.isStatic(method.getModifiers())
85           || Modifier.isPrivate(method.getModifiers())
86           || Modifier.isFinal(method.getModifiers())
87           || skippedMethods.contains(method)) {
88         continue;
89       }
90       Class<?>[] argTypes = method.getParameterTypes();
91       Object[] args = new Object[argTypes.length];
92       for (int i = 0; i < argTypes.length; i++) {
93         if ((args[i] = argProvider.get(method, i, argTypes[i])) == null) {
94           args[i] = Defaults.defaultValue(argTypes[i]);
95         }
96       }
97       method.invoke(forwarder, args);
98       try {
99         method.invoke(verify(mockDelegate), args);
100       } catch (InvocationTargetException e) {
101         AssertionError ae =
102             new AssertionError(String.format("Method was not forwarded: %s", method));
103         ae.initCause(e);
104         throw ae;
105       }
106     }
107 
108     boolean skipToString = false;
109     for (Method method : skippedMethods) {
110       if (method.getName().equals("toString")) {
111         skipToString = true;
112         break;
113       }
114     }
115     if (!skipToString) {
116       String actual = forwarder.toString();
117       String expected =
118           MoreObjects.toStringHelper(forwarder).add("delegate", mockDelegate).toString();
119       assertEquals("Method toString() was not forwarded properly", expected, actual);
120     }
121   }
122 
123   /**
124    * Provides arguments for forwarded methods tested in {@link #testMethodsForwarded}.
125    */
126   public interface ArgumentProvider {
127     /**
128      * Return an instance of the given class to be used as an argument passed to one method call.
129      * If one method has multiple arguments with the same type, each occurrence will call this
130      * method once.  It is recommended that each invocation returns a distinctive object for the
131      * same type, in order to verify that arguments are passed by the tested class correctly.
132      *
133      * @return a value to be passed as an argument.  If {@code null}, {@link Defaults#defaultValue}
134      *         will be used.
135      */
get(Method method, int argPos, Class<?> clazz)136     @Nullable Object get(Method method, int argPos, Class<?> clazz);
137   }
138 }
139