    1   /*
    2    * $Id: InvokeMethodTest.java,v 1.27 2004/05/07 20:02:48 goetze Exp $
    3    *
    4    * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
    5    *
    6    * Redistribution and use of this software and associated documentation
    7    * ("Software"), with or without modification, are permitted provided that the
    8    * following conditions are met:
    9    *  1. Redistributions of source code must retain copyright statements and
    10   * notices. Redistributions must also contain a copy of this document.
    11   *  2. Redistributions in binary form must reproduce the above copyright
    12   * notice, this list of conditions and the following disclaimer in the
    13   * documentation and/or other materials provided with the distribution.
    14   *  3. The name "groovy" must not be used to endorse or promote products
    15   * derived from this Software without prior written permission of The Codehaus.
    16   * For written permission, please contact info@codehaus.org.
    17   *  4. Products derived from this Software may not be called "groovy" nor may
    18   * "groovy" appear in their names without prior written permission of The
    19   * Codehaus. "groovy" is a registered trademark of The Codehaus.
    20   *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
    21   *
    22   * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
    23   * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    24   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    25   * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
    26   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    27   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    28   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    29   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    30   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    31   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
    32   * DAMAGE.
    33   *
    34   */
    35
    36  package org.codehaus.groovy.runtime;
    37
    38  import groovy.lang.GString;
    39  import groovy.lang.GroovyRuntimeException;
    40  import groovy.lang.IntRange;
    41  import groovy.util.GroovyTestCase;
    42
    43  import java.math.BigDecimal;
    44  import java.text.SimpleDateFormat;
    45  import java.util.ArrayList;
    46  import java.util.Arrays;
    47  import java.util.Collection;
    48  import java.util.Date;
    49  import java.util.List;
    50
    51  import junit.framework.AssertionFailedError;
    52
    53  /***
    54   * Tests method invocation
    55   *
    56   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
    57   * @version $Revision: 1.27 $
    58   */
    59  public class InvokeMethodTest extends GroovyTestCase {
    60
    61      protected Invoker invoker = new Invoker();
    62
    63      // Method invocation tests
    64      //-------------------------------------------------------------------------
    65
    66      public void testInvokeMethodNoParams() throws Throwable {
    67          Object value = invoke(this, "mockCallWithNoParams", null);
    68          assertEquals("return value", "NoParams", value);
    69
    70          value = invoke(this, "mockCallWithNoParams", new Object[0]);
    71          assertEquals("return value", "NoParams", value);
    72      }
    73
    74      public void testInvokeMethodOneParam() throws Throwable {
    75          Object value = invoke(this, "mockCallWithOneParam", "abc");
    76          assertEquals("return value", "OneParam", value);
    77      }
    78
    79      public void testInvokeMethodOneParamWhichIsNull() throws Throwable {
    80          Object value = invoke(this, "mockCallWithOneNullParam", new Object[] { null });
    81          assertEquals("return value", "OneParamWithNull", value);
    82
    83          value = invoke(this, "mockCallWithOneNullParam", null);
    84          assertEquals("return value", "OneParamWithNull", value);
    85      }
    86
    87      public void testInvokeOverloadedMethodWithOneParamWhichIsNull() throws Throwable {
    88          Object value = invoke(this, "mockOverloadedMethod", new Object[] { null });
    89          assertEquals("return value", "Object", value);
    90      }
    91
    92      public void testInvokeMethodOneCollectionParameter() throws Throwable {
    93          Object[] foo = { "a", "b", "c" };
    94
    95          Object value = invoke(this, "mockCallWithOneCollectionParam", new Object[] { foo });
    96          assertEquals("return value", new Integer(3), value);
    97
    98          List list = new ArrayList();
    99          list.add("a");
    100         list.add("b");
    101         value = invoke(this, "mockCallWithOneCollectionParam", list);
    102         assertEquals("return value", new Integer(2), value);
    103     }
    104
    105     public void testInvokePrintlnMethod() throws Throwable {
    106         Object value = invoke(System.out, "println", "testing System.out.println...");
    107         assertEquals("return value", null, value);
    108     }
    109
    110     public void testMethodChooserNull() throws Throwable {
    111         assertMethodChooser("Object", new Object[] { null });
    112     }
    113
    114     public void testMethodChooserNoParams() throws Throwable {
    115         assertMethodChooser("void", null);
    116     }
    117
    118     public void testMethodChooserObject() throws Throwable {
    119         assertMethodChooser("Object", new Object());
    120         assertMethodChooser("Object", new Date());
    121     }
    122
    123     public void testMethodChooserString() throws Throwable {
    124         assertMethodChooser("String", "foo");
    125
    126         /*** @todo some type cooercion would be cool here... */
    127         //assertMethodChooser("String", new StringBuffer());
    128         //assertMethodChooser("String", new Character('a');
    129     }
    130
    131     public void testMethodChooserNumber() throws Throwable {
    132         assertMethodChooser("Number", new Integer(2));
    133         assertMethodChooser("Number", new Double(2));
    134     }
    135
    136     public void testMethodChooserTwoParams() throws Throwable {
    137         List list = new ArrayList();
    138         list.add("foo");
    139         list.add("bar");
    140         assertMethodChooser("Object,Object", list.toArray());
    141
    142         Object[] blah = { "a", "b" };
    143         assertMethodChooser("Object,Object", blah);
    144     }
    145
    146     public void testInstanceofWorksForArray() {
    147         Class type = Object[].class;
    148         Object value = new Object[1];
    149         assertTrue("instanceof works for array", type.isInstance(value));
    150     }
    151
    152     public void testMethodChooserTwoParamsWithSecondAnObjectArray() throws Throwable {
    153         Object[] blah = { "a", new Object[] { "b" }
    154         };
    155         assertMethodChooser("Object,Object[]", blah);
    156     }
    157
    158     public void testCollectionMethods() throws Throwable {
    159         Object list = InvokerHelper.createList(new Object[] { "a", "b" });
    160
    161         Object value = invoke(list, "size", null);
    162         assertEquals("size of collection", new Integer(2), value);
    163
    164         value = invoke(list, "contains", "a");
    165         assertEquals("contains method", Boolean.TRUE, value);
    166     }
    167
    168     public void testNewMethods() throws Throwable {
    169         Object value = invoke("hello", "size", null);
    170         assertEquals("size of string", new Integer(5), value);
    171     }
    172
    173     public void testStaticMethod() throws Throwable {
    174         Object value = invoke(DummyBean.class, "dummyStaticMethod", "abc");
    175         assertEquals("size of string", "ABC", value);
    176     }
    177
    178     public void testBaseClassMethod() throws Throwable {
    179         Object object = new DummyBean();
    180         Object value = invoke(object, "toString", null);
    181         assertEquals("toString", object.toString(), value);
    182     }
    183
    184 	//SPG modified to reflect DefaultGroovyMethod name change and expected result from
    185 	//Integer/Integer division.
    186     public void testDivideNumbers() throws Throwable {
    187         assertMethodCall(new Double(10), "div", new Double(2), new Double(5));
    188         assertMethodCall(new Double(10), "div", new Integer(2), new Double(5));
    189         assertMethodCall(new Integer(10), "div", new Double(2), new Double(5));
    190         assertMethodCall(new Integer(10), "div", new Integer(2), new java.math.BigDecimal("5"));
    191     }
    192
    193     public void testBaseFailMethod() throws Throwable {
    194         Object value;
    195         try {
    196             value = invoke(this, "fail", "hello");
    197         }
    198         catch (AssertionFailedError e) {
    199             // worked
    200         }
    201     }
    202
    203     public void testToArrayOnList() throws Throwable {
    204         List object = new ArrayList();
    205         object.add("Hello");
    206
    207         Object[] value = (Object[]) invoke(object, "toArray", null);
    208         assertArrayEquals(object.toArray(), value);
    209         assertEquals(1, value.length);
    210         assertEquals("Hello", value[0]);
    211
    212         value = (Object[]) invoke(object, "toArray", new Object[0]);
    213         assertArrayEquals(object.toArray(), value);
    214     }
    215
    216     public void testInvalidOverloading() throws Throwable {
    217         try {
    218             invoke(this, "badOverload", new Object[] { "a", "b" });
    219             fail("Should fail as an unambiguous method is invoked");
    220         }
    221         catch (GroovyRuntimeException e) {
    222             System.out.println("Caught: " + e);
    223         }
    224     }
    225
    226     public void testPlusWithNull() throws Throwable {
    227         String param = "called with: ";
    228         Object value = invoke(param, "plus", new Object[] { null });
    229         assertEquals("called with null", param + null, value);
    230     }
    231
    232     public void testCallIntMethodWithInteger() throws Throwable {
    233         Object value = invoke(this, "overloadedRemove", new Object[] { new Integer(5)});
    234         assertEquals("called with integer", "int5", value);
    235     }
    236
    237     public void testCallListRemove() throws Throwable {
    238         List list = new ArrayList();
    239         list.add("foo");
    240         list.add("bar");
    241
    242         Object value = invoke(list, "remove", new Object[] { new Integer(0)});
    243
    244         assertEquals("Should have just 1 item left: " + list, 1, list.size());
    245     }
    246
    247     public void testCoerceGStringToString() throws Throwable {
    248         GString param = new GString(new Object[] { "James" }) {
    249             public String[] getStrings() {
    250                 return new String[] { "Hello " };
    251             }
    252         };
    253         Object value = invoke(this, "methodTakesString", new Object[] { param });
    254         assertEquals("converted GString to string", param.toString(), value);
    255     }
    256
    257     public void testCoerceGStringToStringOnGetBytes() throws Throwable {
    258         GString param = new GString(new Object[] { "US-ASCII" }) {
    259             public String[] getStrings() {
    260                 return new String[] { "" };
    261             }
    262         };
    263         Object value = invoke("test", "getBytes", new Object[] { param });
    264         assertEquals("converted GString to string", "test".getBytes("US-ASCII").getClass(), value.getClass());
    265     }
    266
    267     public void testBadBDToDoubleCoerce() throws Throwable {
    268         try {
    269             Object value = invoke(Math.class, "floor", new BigDecimal("1.7E309"));
    270         } catch (GroovyRuntimeException e) {
    271             assertTrue("Math.floor(1.7E309) should fail because it is out of range for a Double. "
    272                     +e,e.getMessage().indexOf("out of range") > 0);
    273             return;
    274         }
    275         fail("Math.floor(1.7E309) should fail because it is out of range for a Double.");
    276     }
    277
    278     public void testClassMethod() throws Throwable {
    279         Class c = String.class;
    280         Object value = invoke(c, "getName", null);
    281         assertEquals("Class.getName()", c.getName(), value);
    282         c = getClass();
    283         value = invoke(c, "getName", null);
    284         assertEquals("Class.getName()", c.getName(), value);
    285     }
    286
    287     public void testProtectedMethod() throws Throwable {
    288         String param = "hello";
    289         Object value = invoke(this, "aProtectedMethod", param);
    290         assertEquals("protected method call", aProtectedMethod(param), value);
    291     }
    292
    293     public void testPrivateMethod() throws Throwable {
    294         String param = "hello";
    295         Object value = invoke(this, "aPrivateMethod", param);
    296         assertEquals("private method call", aPrivateMethod(param), value);
    297     }
    298
    299     public void testStringSubstringMethod() throws Throwable {
    300         String object = "hello";
    301         Object value = invoke(object, "substring", new Integer(2));
    302         assertEquals("substring(2)", object.substring(2), value);
    303
    304         value = invoke(object, "substring", new Object[] { new Integer(1), new Integer(3)});
    305         assertEquals("substring(1,3)", object.substring(1, 3), value);
    306     }
    307
    308     public void testListGetWithRange() throws Throwable {
    309         List list = Arrays.asList(new Object[] { "a", "b", "c" });
    310         Object range = new IntRange(0, 2);
    311         Object value = invoke(list, "getAt", range);
    312         assertTrue("Returned List: " + value, value instanceof List);
    313         List retList = (List) value;
    314         assertEquals("List size", 3, retList.size());
    315     }
    316
    317     public void testSetLenientOnDateFormat() throws Throwable {
    318         SimpleDateFormat a = new SimpleDateFormat( "MM/dd/yyyy" );
    319
    320         Object value = invoke(a, "setLenient", new Object[] { Boolean.FALSE });
    321         assertEquals("void method", null, value);
    322     }
    323
    324     public void testInvokeUnknownMethod() throws Throwable {
    325         try {
    326             Object value = invoke(this, "unknownMethod", "abc");
    327             fail("Should have thrown an exception");
    328         }
    329         catch (GroovyRuntimeException e) {
    330             // worked
    331         }
    332     }
    333
    334     public void testInvokeMethodWithWrongNumberOfParameters() throws Throwable {
    335         try {
    336             Object[] args = { "a", "b" };
    337             Object value = invoke(this, "unknownMethod", args);
    338             fail("Should have thrown an exception");
    339         }
    340         catch (GroovyRuntimeException e) {
    341             // worked
    342         }
    343     }
    344
    345     public void testInvokeMethodOnNullObject() throws Throwable {
    346         try {
    347             invoke(null, "mockCallWithNoParams", null);
    348             fail("Should have thrown an exception");
    349         }
    350         catch (NullPointerException e) {
    351             // worked
    352         }
    353     }
    354
    355     // Mock methods used for testing
    356     //-------------------------------------------------------------------------
    357
    358     public Object mockCallWithNoParams() {
    359         return "NoParams";
    360     }
    361
    362     public Object mockCallWithOneParam(Object value) {
    363         assertEquals("Method not passed in the correct value", "abc", value);
    364         return "OneParam";
    365     }
    366
    367     public Object mockCallWithOneNullParam(Object value) {
    368         assertEquals("Method not passed in the correct value", null, value);
    369         return "OneParamWithNull";
    370     }
    371
    372     public Integer mockCallWithOneCollectionParam(Object collection) {
    373         Collection coll = InvokerHelper.asCollection(collection);
    374         return new Integer(coll.size());
    375     }
    376
    377     public Object mockOverloadedMethod() {
    378         return "void";
    379     }
    380
    381     public Object mockOverloadedMethod(Object object) {
    382         return "Object";
    383     }
    384
    385     public Object mockOverloadedMethod(Number object) {
    386         return "Number";
    387     }
    388
    389     public Object mockOverloadedMethod(String object) {
    390         return "String";
    391     }
    392
    393     public Object mockOverloadedMethod(Object object, Object bar) {
    394         return "Object,Object";
    395     }
    396
    397     public Object mockOverloadedMethod(Object object, Object[] array) {
    398         return "Object,Object[]";
    399     }
    400
    401     public Object badOverload(String a, Object b) {
    402         return "String, Object";
    403     }
    404
    405     public Object badOverload(Object a, String b) {
    406         return "Object, String";
    407     }
    408
    409     public Object methodTakesString(String x) {
    410         return x;
    411     }
    412
    413     public Object overloadedRemove(int idx) {
    414         return "int" + idx;
    415     }
    416
    417     public Object overloadedRemove(Object value) {
    418         return "Object" + value;
    419     }
    420
    421     // Implementation methods
    422     //-------------------------------------------------------------------------
    423
    424     protected Object aProtectedMethod(String param) {
    425         return param + " there!";
    426     }
    427
    428     private Object aPrivateMethod(String param) {
    429         return param + " James!";
    430     }
    431
    432     protected void assertMethodCall(Object object, String method, Object param, Object expected) {
    433         Object value = InvokerHelper.invokeMethod(object, method, new Object[] { param });
    434         assertEquals("result of method: " + method, expected, value);
    435     }
    436
    437     /***
    438 	 * Asserts that invoking the method chooser finds the right overloaded
    439 	 * method implementation
    440 	 *
    441 	 * @param expected
    442 	 *            is the expected value of the method
    443 	 * @param arguments
    444 	 *            the argument(s) to the method invocation
    445 	 */
    446     protected void assertMethodChooser(Object expected, Object arguments) throws Throwable {
    447         Object value = invoke(this, "mockOverloadedMethod", arguments);
    448
    449         assertEquals("Invoking overloaded method for arguments: " + InvokerHelper.toString(arguments), expected, value);
    450     }
    451
    452     protected Object invoke(Object object, String method, Object args) throws Throwable {
    453         try {
    454             return invoker.invokeMethod(object, method, args);
    455         }
    456         catch (InvokerInvocationException e) {
    457             throw e.getCause();
    458         }
    459     }
    460 }