    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 }