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