1   /*
2    * Copyright 2001-2004 The Apache Software Foundation.
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 org.apache.commons.beanutils;
18  
19  
20  import java.io.OutputStream;
21  import java.io.PrintStream;
22  
23  import java.lang.reflect.Method;
24  import java.lang.reflect.Modifier;
25  
26  import org.apache.commons.beanutils.priv.PrivateBeanFactory;
27  import org.apache.commons.beanutils.priv.PublicSubBean;
28  
29  import junit.framework.TestCase;
30  import junit.framework.Test;
31  import junit.framework.TestSuite;
32  
33  
34  /**
35   * <p> Test case for <code>MethodUtils</code> </p>
36   *
37   */
38  public class MethodUtilsTestCase extends TestCase {
39  
40      // ---------------------------------------------------- Instance Variables
41  
42      protected PrivateBeanFactory privateBeanFactory;
43  
44      // ---------------------------------------------------------- Constructors
45  
46      /**
47       * Construct a new instance of this test case.
48       *
49       * @param name Name of the test case
50       */
51      public MethodUtilsTestCase(String name) {
52          super(name);
53      }
54  
55  
56      // -------------------------------------------------- Overall Test Methods
57  
58  
59      /**
60       * Set up instance variables required by this test case.
61       */
62      public void setUp() {
63          privateBeanFactory = new PrivateBeanFactory();
64      }
65  
66  
67      /**
68       * Return the tests included in this test suite.
69       */
70      public static Test suite() {
71          return (new TestSuite(MethodUtilsTestCase.class));
72      }
73  
74      /**
75       * Tear down instance variables required by this test case.
76       */
77      public void tearDown() {
78          privateBeanFactory = null;
79      }
80  
81  
82      // ------------------------------------------------ Individual Test Methods
83  
84      /**
85       * <p> Test <code>getAccessibleMethod</code>.
86       */
87      public void testGetAccessibleMethod() {
88          // test MethodUtils.getAccessibleMethod
89          // we'll make things easier by using the convenience methods
90  
91          // easy bit first - find a public method
92          // METHOD ONE
93          Method method = MethodUtils.getAccessibleMethod
94                  (TestBean.class, "setStringProperty", String.class);
95  
96          // check that we've found one that matches
97          assertNotNull(method);
98          assertEquals("method ONE is named correctly",
99                  "setStringProperty", method.getName());
100         assertTrue("Method ONE is public",
101                 Modifier.isPublic(method.getModifiers()));
102 
103         // trickier this one - find a method in a direct interface
104         // METHOD TWO
105         method = MethodUtils.getAccessibleMethod
106                 (privateBeanFactory.create().getClass(),
107                         "methodBar",
108                         String.class);
109 
110         // check that we've found one that matches
111         assertNotNull(method);
112         assertEquals("Method TWO is named correctly",
113                 "methodBar", method.getName());
114         assertTrue("Method TWO is public",
115                 Modifier.isPublic(method.getModifiers()));
116 
117         // trickier this one - find a method in a indirect interface
118         // METHOD THREE
119         method = MethodUtils.getAccessibleMethod
120                 (privateBeanFactory.createSubclass().getClass(),
121                         "methodBaz",
122                         String.class);
123 
124         // check that we've found one that matches
125         assertNotNull(method);
126         assertEquals("Method THREE is named correctly",
127                 "methodBaz", method.getName());
128         assertTrue("Method THREE is public",
129                 Modifier.isPublic(method.getModifiers()));
130 
131     }
132 
133 
134     /**
135      * <p> Test <code>invokeExactMethod</code>.
136      */
137     public void testInvokeExactMethod() {
138         // test MethodUtils.invokeExactMethod
139         // easy bit first - invoke a public method
140         // METHOD ONE
141         try {
142 
143             TestBean bean = new TestBean();
144             Object ret = MethodUtils.invokeExactMethod(bean, "setStringProperty", "TEST");
145             // check that the return's right and that the properties been set
146             assertNull(ret);
147             assertEquals("Method ONE was invoked", "TEST", bean.getStringProperty());
148 
149         } catch (Throwable t) {
150             // ONE
151             fail("Exception in method ONE prevented invokation: " + t.toString());
152         }
153 
154         // trickier this one - find a method in a direct interface
155         // METHOD TWO FAILURE
156         try {
157 
158             Object ret = MethodUtils.invokeExactMethod(
159                     privateBeanFactory.create(),
160                     "methodBar",
161                     "ANOTHER TEST");
162 
163             // check that we've found one that matches
164             assertEquals("Method TWO was invoked correctly", "ANOTHER TEST", ret);
165 
166         } catch (Throwable t) {
167             // METHOD TWO FAILURE
168             fail("Exception in method TWO prevented invokation: " + t.toString());
169         }
170 
171 
172         // trickier this one - find a method in a indirect interface
173         // METHOD THREE
174         try {
175 
176             Object ret = MethodUtils.invokeExactMethod(
177                     privateBeanFactory.createSubclass(),
178                     "methodBaz",
179                     "YET ANOTHER TEST");
180 
181 
182             // check that we've found one that matches
183             assertEquals("Method TWO was invoked correctly", "YET ANOTHER TEST", ret);
184 
185 
186         } catch (Throwable t) {
187             // METHOD THREE FAILURE
188             fail("Exception in method THREE prevented invokation: " + t.toString());
189 
190         }
191     }
192     
193     /**
194      * <p> Test <code>invokeMethod</code>.
195      */
196     public void testInvokeMethod() throws Exception {
197         // i'm going to test that the actual calls work first and then try them via reflection
198         
199         AbstractParent parent = new AlphaBean("parent");
200         
201         // try testAddChild through abstract superclass
202         BetaBean childOne = new BetaBean("ChildOne");
203         
204         assertEquals("Oh no! Badly coded test case! (1)", "ChildOne", parent.testAddChild(childOne));
205         
206         // let's try MethodUtils version
207         assertEquals(
208                         "Cannot invoke through abstract class (1)", 
209                         "ChildOne", 
210                         MethodUtils.invokeMethod(parent, "testAddChild", childOne));
211 
212         
213         // try adding through interface
214         AlphaBean childTwo = new AlphaBean("ChildTwo");
215         
216         assertEquals("Oh no! Badly coded test case! (2)", "ChildTwo", parent.testAddChild(childTwo));
217         
218         // let's try MethodUtils version
219         assertEquals(
220                         "Cannot invoke through interface (1)", 
221                         "ChildTwo", 
222                         MethodUtils.invokeMethod(parent, "testAddChild", childTwo));
223        
224         
225         Object[] params = new Object[2];
226 
227         assertEquals("Oh no! Badly coded test case! (3)", "ChildOne", parent.testAddChild2("parameter", childOne));
228         
229         
230         // let's try MethodUtils version
231         params[0] = "parameter";
232         params[1] = childOne;
233         
234         assertEquals(
235                         "Cannot invoke through abstract class (1)", 
236                         "ChildOne", 
237                         MethodUtils.invokeMethod(parent, "testAddChild2", params));
238                         
239         assertEquals("Oh no! Badly coded test case! (4)", "ChildTwo", parent.testAddChild2("parameter", childTwo));
240         
241         // let's try MethodUtils version
242         params[0] = "parameter";
243         params[1] = childTwo;
244        
245         assertEquals(
246                         "Cannot invoke through abstract class (1)", 
247                         "ChildTwo", 
248                         MethodUtils.invokeMethod(parent, "testAddChild2", params));
249         
250         // test that exception is correctly thrown when a method cannot be found with matching params
251         try {
252             // the next line
253             parent = new AlphaBean("parent");
254             childOne = new BetaBean("ChildOne");
255             MethodUtils.invokeMethod(parent, "bogus", childOne);
256             // should get here!
257             fail("No exception thrown when no appropriate method exists");
258             
259         } catch (NoSuchMethodException e) {
260             // this is what we're expecting!
261         }
262         
263         MethodUtils.invokeMethod(parent, "getName", null);
264         MethodUtils.invokeMethod(parent, "getName", null, null);
265         MethodUtils.invokeExactMethod(parent, "getName", null);
266         MethodUtils.invokeExactMethod(parent, "getName", null, null);        
267     }
268 
269     
270     /**
271      * <p> Test <code>invokeMethod</code> with a primitive.
272      */
273     public void testInvokeMethodWithPrimitives() throws Exception {
274         // first test that the bean works 
275         PrimitiveBean bean = new PrimitiveBean();
276         bean.setFloat(20.0f);
277         bean.setLong(10l);
278         bean.setBoolean(true);
279         bean.setInt(12);
280         bean.setDouble(25.5d);
281         
282         assertEquals("Bug in PrimitiveBean (1)", 20.0f, bean.getFloat(), 0.01f);
283         assertEquals("Bug in PrimitiveBean (2)", 10, bean.getLong());
284         assertEquals("Bug in PrimitiveBean (3)", true, bean.getBoolean());
285         assertEquals("Bug in PrimitiveBean (4)", 12, bean.getInt());
286         assertEquals("Bug in PrimitiveBean (5)", 25.5d, bean.getDouble(), 0.01f);
287         
288         bean = new PrimitiveBean();
289         MethodUtils.invokeMethod(bean, "setBoolean", new Boolean(true));
290         assertEquals("Call boolean property using invokeMethod", true, bean.getBoolean());
291 
292         bean = new PrimitiveBean();
293         MethodUtils.invokeMethod(bean, "setFloat", new Float(20.0f));
294         assertEquals("Call float property using invokeMethod", 20.0f, bean.getFloat(), 0.01f);
295         
296         bean = new PrimitiveBean();
297         MethodUtils.invokeMethod(bean, "setLong", new Long(10));
298         assertEquals("Call float property using invokeMethod", 10, bean.getLong());
299         
300         bean = new PrimitiveBean();
301         MethodUtils.invokeMethod(bean, "setInt", new Integer(12));
302         assertEquals("Set float property using invokeMethod", 12, bean.getInt());
303         
304         bean = new PrimitiveBean();
305         MethodUtils.invokeMethod(bean, "setDouble", new Double(25.5d));
306         assertEquals("Set float property using invokeMethod", 25.5d, bean.getDouble(), 0.01d);
307     }
308 
309 
310     /**
311      * Simple tests for accessing static methods via invokeMethod().
312      */
313     public void testSimpleStatic1() {
314 
315         TestBean bean = new TestBean();
316         Object value = null;
317         int current = TestBean.currentCounter();
318 
319         try {
320 
321             // Return initial value of the counter
322             value = MethodUtils.invokeMethod
323                 (bean, "currentCounter", new Object[0], new Class[0]);
324             assertNotNull("currentCounter exists", value);
325             assertTrue("currentCounter type",
326                        value instanceof Integer);
327             assertEquals("currentCounter value",
328                          current,
329                          ((Integer) value).intValue());
330 
331             // Increment via no-arguments version
332             MethodUtils.invokeMethod
333                 (bean, "incrementCounter", new Object[0], new Class[0]);
334 
335             // Validate updated value
336             current++;
337             value = MethodUtils.invokeMethod
338                 (bean, "currentCounter", new Object[0], new Class[0]);
339             assertNotNull("currentCounter exists", value);
340             assertTrue("currentCounter type",
341                        value instanceof Integer);
342             assertEquals("currentCounter value",
343                          current,
344                          ((Integer) value).intValue());
345 
346             // Increment via specified-argument version
347             MethodUtils.invokeMethod
348                 (bean, "incrementCounter",
349                  new Object[] { new Integer(5) },
350                  new Class[] { Integer.TYPE });
351 
352             // Validate updated value
353             current += 5;
354             value = MethodUtils.invokeMethod
355                 (bean, "currentCounter", new Object[0], new Class[0]);
356             assertNotNull("currentCounter exists", value);
357             assertTrue("currentCounter type",
358                        value instanceof Integer);
359             assertEquals("currentCounter value",
360                          current,
361                          ((Integer) value).intValue());
362 
363         } catch (Exception e) {
364             fail("Threw exception" + e);
365         }
366 
367     }
368 
369 
370     /**
371      * Simple tests for accessing static methods via invokeExactMethod().
372      */
373     public void testSimpleStatic2() {
374 
375         TestBean bean = new TestBean();
376         Object value = null;
377         int current = TestBean.currentCounter();
378 
379         try {
380 
381             // Return initial value of the counter
382             value = MethodUtils.invokeExactMethod
383                 (bean, "currentCounter", new Object[0], new Class[0]);
384             assertNotNull("currentCounter exists", value);
385             assertTrue("currentCounter type",
386                        value instanceof Integer);
387             assertEquals("currentCounter value",
388                          current,
389                          ((Integer) value).intValue());
390 
391             // Increment via no-arguments version
392             MethodUtils.invokeExactMethod
393                 (bean, "incrementCounter", new Object[0], new Class[0]);
394 
395             // Validate updated value
396             current++;
397             value = MethodUtils.invokeExactMethod
398                 (bean, "currentCounter", new Object[0], new Class[0]);
399             assertNotNull("currentCounter exists", value);
400             assertTrue("currentCounter type",
401                        value instanceof Integer);
402             assertEquals("currentCounter value",
403                          current,
404                          ((Integer) value).intValue());
405 
406             // Increment via specified-argument version
407             MethodUtils.invokeExactMethod
408                 (bean, "incrementCounter",
409                  new Object[] { new Integer(5) },
410                  new Class[] { Integer.TYPE });
411 
412             // Validate updated value
413             current += 5;
414             value = MethodUtils.invokeExactMethod
415                 (bean, "currentCounter", new Object[0], new Class[0]);
416             assertNotNull("currentCounter exists", value);
417             assertTrue("currentCounter type",
418                        value instanceof Integer);
419             assertEquals("currentCounter value",
420                          current,
421                          ((Integer) value).intValue());
422 
423 
424         } catch (Exception e) {
425             fail("Threw exception" + e);
426         }
427 
428     }
429 
430 
431     /**
432      * Simple tests for accessing static methods via getAccessibleMethod()
433      */
434     public void testSimpleStatic3() {
435 
436         Object value = null;
437         int current = TestBean.currentCounter();
438 
439         try {
440 
441             // Acquire the methods we need
442             Method currentCounterMethod = MethodUtils.getAccessibleMethod
443                 (TestBean.class, "currentCounter",
444                  new Class[0]);
445             assertNotNull("currentCounterMethod exists",
446                           currentCounterMethod);
447             assertEquals("currentCounterMethod name",
448                          "currentCounter",
449                          currentCounterMethod.getName());
450             assertEquals("currentCounterMethod args",
451                          0,
452                          currentCounterMethod.getParameterTypes().length);
453             assertTrue("currentCounterMethod public",
454                        Modifier.isPublic(currentCounterMethod.getModifiers()));
455             assertTrue("currentCounterMethod static",
456                        Modifier.isStatic(currentCounterMethod.getModifiers()));
457             Method incrementCounterMethod1 = MethodUtils.getAccessibleMethod
458                 (TestBean.class, "incrementCounter",
459                  new Class[0]);
460             assertNotNull("incrementCounterMethod1 exists",
461                           incrementCounterMethod1);
462             assertEquals("incrementCounterMethod1 name",
463                          "incrementCounter",
464                          incrementCounterMethod1.getName());
465             assertEquals("incrementCounterMethod1 args",
466                          0,
467                          incrementCounterMethod1.getParameterTypes().length);
468             assertTrue("incrementCounterMethod1 public",
469                        Modifier.isPublic(incrementCounterMethod1.getModifiers()));
470             assertTrue("incrementCounterMethod1 static",
471                        Modifier.isStatic(incrementCounterMethod1.getModifiers()));
472             Method incrementCounterMethod2 = MethodUtils.getAccessibleMethod
473                 (TestBean.class, "incrementCounter",
474                  new Class[] { Integer.TYPE });
475             assertNotNull("incrementCounterMethod2 exists",
476                           incrementCounterMethod2);
477             assertEquals("incrementCounterMethod2 name",
478                          "incrementCounter",
479                          incrementCounterMethod2.getName());
480             assertEquals("incrementCounterMethod2 args",
481                          1,
482                          incrementCounterMethod2.getParameterTypes().length);
483             assertTrue("incrementCounterMethod2 public",
484                        Modifier.isPublic(incrementCounterMethod2.getModifiers()));
485             assertTrue("incrementCounterMethod2 static",
486                        Modifier.isStatic(incrementCounterMethod2.getModifiers()));
487 
488             // Return initial value of the counter
489             value = currentCounterMethod.invoke(null, new Object[0]);
490             assertNotNull("currentCounter exists", value);
491             assertTrue("currentCounter type",
492                        value instanceof Integer);
493             assertEquals("currentCounter value",
494                          current,
495                          ((Integer) value).intValue());
496 
497             // Increment via no-arguments version
498             incrementCounterMethod1.invoke(null, new Object[0]);
499 
500             // Validate updated value
501             current++;
502             value = currentCounterMethod.invoke(null, new Object[0]);
503             assertNotNull("currentCounter exists", value);
504             assertTrue("currentCounter type",
505                        value instanceof Integer);
506             assertEquals("currentCounter value",
507                          current,
508                          ((Integer) value).intValue());
509 
510             // Increment via specified-argument version
511             incrementCounterMethod2.invoke(null,
512                                            new Object[] { new Integer(5) });
513 
514             // Validate updated value
515             current += 5;
516             value = currentCounterMethod.invoke(null, new Object[0]);
517             assertNotNull("currentCounter exists", value);
518             assertTrue("currentCounter type",
519                        value instanceof Integer);
520             assertEquals("currentCounter value",
521                          current,
522                          ((Integer) value).intValue());
523 
524         } catch (Exception e) {
525             fail("Threw exception" + e);
526         }
527 
528     }
529 
530     public void testPublicSub() throws Exception {
531         // make sure that bean does what it should
532         PublicSubBean bean = new PublicSubBean();
533         assertEquals("Start value (foo)", bean.getFoo(), "This is foo");
534         assertEquals("Start value (bar)", bean.getBar(), "This is bar");
535         bean.setFoo("new foo");
536         bean.setBar("new bar");
537         assertEquals("Set value (foo)", bean.getFoo(), "new foo");
538         assertEquals("Set value (bar)", bean.getBar(), "new bar");
539         
540         // see if we can access public methods in a default access superclass
541         // from a public access subclass instance
542         MethodUtils.invokeMethod(bean, "setFoo", "alpha");
543         assertEquals("Set value (foo:2)", bean.getFoo(), "alpha");
544         MethodUtils.invokeMethod(bean, "setBar", "beta");
545         assertEquals("Set value (bar:2)", bean.getFoo(), "alpha");
546     }
547     
548     public void testParentMethod() throws Exception {
549         OutputStream os = new PrintStream(System.out);
550         PrintStream ps = new PrintStream(System.out);
551         
552         A a = new A();
553         MethodUtils.invokeMethod(a, "foo", os);
554         assertTrue("Method Invoked(1)", a.called);
555         
556         a = new A();
557         MethodUtils.invokeMethod(a, "foo", ps);
558         assertTrue("Method Invoked(2)", a.called);
559     }
560 }