    1   /*
    2    * $Id: InvokeMethodTest.java,v 1.32 2006/03/03 00:39:20 blackdrag 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.32 $
    58   */
    59  public class InvokeMethodTest extends GroovyTestCase {
    60
    61      protected Invoker invoker = InvokerHelper.getInstance();
    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_FAILS() throws Throwable { if (notYetImplemented()) return;
    124         assertMethodChooser("String", "foo");
    125         assertMethodChooser("String", new StringBuffer());
    126         assertMethodChooser("String", new Character('a'));
    127     }
    128
    129     public void testMethodChooserNumber() throws Throwable {
    130         assertMethodChooser("Number", new Integer(2));
    131         assertMethodChooser("Number", new Double(2));
    132     }
    133
    134     public void testMethodChooserTwoParams() throws Throwable {
    135         List list = new ArrayList();
    136         list.add("foo");
    137         list.add("bar");
    138         assertMethodChooser("Object,Object", list.toArray());
    139
    140         Object[] blah = { "a", "b" };
    141         assertMethodChooser("Object,Object", blah);
    142     }
    143
    144     public void testInstanceofWorksForArray() {
    145         Class type = Object[].class;
    146         Object value = new Object[1];
    147         assertTrue("instanceof works for array", type.isInstance(value));
    148     }
    149
    150     public void testMethodChooserTwoParamsWithSecondAnObjectArray() throws Throwable {
    151         Object[] blah = { "a", new Object[] { "b" }
    152         };
    153         assertMethodChooser("Object,Object[]", blah);
    154     }
    155
    156     public void testCollectionMethods() throws Throwable {
    157         Object list = InvokerHelper.createList(new Object[] { "a", "b" });
    158
    159         Object value = invoke(list, "size", null);
    160         assertEquals("size of collection", new Integer(2), value);
    161
    162         value = invoke(list, "contains", "a");
    163         assertEquals("contains method", Boolean.TRUE, value);
    164     }
    165
    166     public void testNewMethods() throws Throwable {
    167         Object value = invoke("hello", "size", null);
    168         assertEquals("size of string", new Integer(5), value);
    169     }
    170
    171     public void testStaticMethod() throws Throwable {
    172         Object value = invoke(DummyBean.class, "dummyStaticMethod", "abc");
    173         assertEquals("size of string", "ABC", value);
    174     }
    175
    176     public void testBaseClassMethod() throws Throwable {
    177         Object object = new DummyBean();
    178         Object value = invoke(object, "toString", null);
    179         assertEquals("toString", object.toString(), value);
    180     }
    181
    182 	//SPG modified to reflect DefaultGroovyMethod name change and expected result from
    183 	//Integer/Integer division.
    184     public void testDivideNumbers() throws Throwable {
    185         assertMethodCall(new Double(10), "div", new Double(2), new Double(5));
    186         assertMethodCall(new Double(10), "div", new Integer(2), new Double(5));
    187         assertMethodCall(new Integer(10), "div", new Double(2), new Double(5));
    188         assertMethodCall(new Integer(10), "div", new Integer(2), new java.math.BigDecimal("5"));
    189     }
    190
    191     public void testBaseFailMethod() throws Throwable {
    192         Object value;
    193         try {
    194             value = invoke(this, "fail", "hello");
    195         }
    196         catch (AssertionFailedError e) {
    197             // worked
    198         }
    199     }
    200
    201     public void testToArrayOnList() throws Throwable {
    202         List object = new ArrayList();
    203         object.add("Hello");
    204
    205         Object[] value = (Object[]) invoke(object, "toArray", null);
    206         assertArrayEquals(object.toArray(), value);
    207         assertEquals(1, value.length);
    208         assertEquals("Hello", value[0]);
    209
    210         value = (Object[]) invoke(object, "toArray", new Object[0]);
    211         assertArrayEquals(object.toArray(), value);
    212     }
    213
    214     public void testInvalidOverloading() throws Throwable {
    215         try {
    216             invoke(this, "badOverload", new Object[] { "a", "b" });
    217             fail("Should fail as an unambiguous method is invoked");
    218         }
    219         catch (GroovyRuntimeException e) {
    220             System.out.println("Caught: " + e);
    221         }
    222     }
    223
    224     public void testPlusWithNull() throws Throwable {
    225         String param = "called with: ";
    226         Object value = invoke(param, "plus", new Object[] { null });
    227         assertEquals("called with null", param + null, value);
    228     }
    229
    230     public void testCallIntMethodWithInteger() throws Throwable {
    231         Object value = invoke(this, "overloadedRemove", new Object[] { new Integer(5)});
    232         assertEquals("called with integer", "int5", value);
    233     }
    234
    235     public void testCallListRemove() throws Throwable {
    236         List list = new ArrayList();
    237         list.add("foo");
    238         list.add("bar");
    239
    240         Object value = invoke(list, "remove", new Object[] { new Integer(0)});
    241
    242         assertEquals("Should have just 1 item left: " + list, 1, list.size());
    243     }
    244
    245     public void testCoerceGStringToString() throws Throwable {
    246         GString param = new GString(new Object[] { "James" }) {
    247             public String[] getStrings() {
    248                 return new String[] { "Hello " };
    249             }
    250         };
    251         Object value = invoke(this, "methodTakesString", new Object[] { param });
    252         assertEquals("converted GString to string", param.toString(), value);
    253     }
    254
    255     public void testCoerceGStringToStringOnGetBytes() throws Throwable {
    256         GString param = new GString(new Object[] { "US-ASCII" }) {
    257             public String[] getStrings() {
    258                 return new String[] { "" };
    259             }
    260         };
    261         Object value = invoke("test", "getBytes", new Object[] { param });
    262         assertEquals("converted GString to string", "test".getBytes("US-ASCII").getClass(), value.getClass());
    263     }
    264
    265     public void testBadBDToDoubleCoerce() throws Throwable {
    266         try {
    267             Object value = invoke(Math.class, "floor", new BigDecimal("1.7E309"));
    268         } catch (IllegalArgumentException e) {
    269             assertTrue("Math.floor(1.7E309) should fail because it is out of range for a Double. "
    270                     +e,e.getMessage().indexOf("out of range") > 0);
    271             return;
    272         }
    273         fail("Math.floor(1.7E309) should fail because it is out of range for a Double.");
    274     }
    275
    276     public void testClassMethod() throws Throwable {
    277         Class c = String.class;
    278         Object value = invoke(c, "getName", null);
    279         assertEquals("Class.getName()", c.getName(), value);
    280         c = getClass();
    281         value = invoke(c, "getName", null);
    282         assertEquals("Class.getName()", c.getName(), value);
    283     }
    284
    285     public void testProtectedMethod() throws Throwable {
    286         String param = "hello";
    287         Object value = invoke(this, "aProtectedMethod", param);
    288         assertEquals("protected method call", aProtectedMethod(param), value);
    289     }
    290
    291     public void testPrivateMethod() throws Throwable {
    292         String param = "hello";
    293         Object value = invoke(this, "aPrivateMethod", param);
    294         assertEquals("private method call", aPrivateMethod(param), value);
    295     }
    296
    297     public void testStringSubstringMethod() throws Throwable {
    298         String object = "hello";
    299         Object value = invoke(object, "substring", new Integer(2));
    300         assertEquals("substring(2)", object.substring(2), value);
    301
    302         value = invoke(object, "substring", new Object[] { new Integer(1), new Integer(3)});
    303         assertEquals("substring(1,3)", object.substring(1, 3), value);
    304     }
    305
    306     public void testListGetWithRange() throws Throwable {
    307         List list = Arrays.asList(new Object[] { "a", "b", "c" });
    308         Object range = new IntRange(0, 2);
    309         Object value = invoke(list, "getAt", range);
    310         assertTrue("Returned List: " + value, value instanceof List);
    311         List retList = (List) value;
    312         assertEquals("List size", 3, retList.size());
    313     }
    314
    315     public void testSetLenientOnDateFormat() throws Throwable {
    316         SimpleDateFormat a = new SimpleDateFormat( "MM/dd/yyyy" );
    317
    318         Object value = invoke(a, "setLenient", new Object[] { Boolean.FALSE });
    319         assertEquals("void method", null, value);
    320     }
    321
    322     public void testInvokeUnknownMethod() throws Throwable {
    323         try {
    324             Object value = invoke(this, "unknownMethod", "abc");
    325             fail("Should have thrown an exception");
    326         }
    327         catch (GroovyRuntimeException e) {
    328             // worked
    329         }
    330     }
    331
    332     public void testInvokeMethodWithWrongNumberOfParameters() throws Throwable {
    333         try {
    334             Object[] args = { "a", "b" };
    335             Object value = invoke(this, "unknownMethod", args);
    336             fail("Should have thrown an exception");
    337         }
    338         catch (GroovyRuntimeException e) {
    339             // worked
    340         }
    341     }
    342
    343     public void testInvokeMethodOnNullObject() throws Throwable {
    344         try {
    345             invoke(null, "mockCallWithNoParams", null);
    346             fail("Should have thrown an exception");
    347         }
    348         catch (NullPointerException e) {
    349             // worked
    350         }
    351     }
    352
    353     // Mock methods used for testing
    354     //-------------------------------------------------------------------------
    355
    356     public Object mockCallWithNoParams() {
    357         return "NoParams";
    358     }
    359
    360     public Object mockCallWithOneParam(Object value) {
    361         assertEquals("Method not passed in the correct value", "abc", value);
    362         return "OneParam";
    363     }
    364
    365     public Object mockCallWithOneNullParam(Object value) {
    366         assertEquals("Method not passed in the correct value", null, value);
    367         return "OneParamWithNull";
    368     }
    369
    370     public Integer mockCallWithOneCollectionParam(Object collection) {
    371         Collection coll = InvokerHelper.asCollection(collection);
    372         return new Integer(coll.size());
    373     }
    374
    375     public Object mockOverloadedMethod() {
    376         return "void";
    377     }
    378
    379     public Object mockOverloadedMethod(Object object) {
    380         return "Object";
    381     }
    382
    383     public Object mockOverloadedMethod(Number object) {
    384         return "Number";
    385     }
    386
    387     public Object mockOverloadedMethod(String object) {
    388         return "String";
    389     }
    390
    391     public Object mockOverloadedMethod(Object object, Object bar) {
    392         return "Object,Object";
    393     }
    394
    395     public Object mockOverloadedMethod(Object object, Object[] array) {
    396         return "Object,Object[]";
    397     }
    398
    399     public Object badOverload(String a, Object b) {
    400         return "String, Object";
    401     }
    402
    403     public Object badOverload(Object a, String b) {
    404         return "Object, String";
    405     }
    406
    407     public Object methodTakesString(String x) {
    408         return x;
    409     }
    410
    411     public Object overloadedRemove(int idx) {
    412         return "int" + idx;
    413     }
    414
    415     public Object overloadedRemove(Object value) {
    416         return "Object" + value;
    417     }
    418
    419     // Implementation methods
    420     //-------------------------------------------------------------------------
    421
    422     protected Object aProtectedMethod(String param) {
    423         return param + " there!";
    424     }
    425
    426     private Object aPrivateMethod(String param) {
    427         return param + " James!";
    428     }
    429
    430     protected void assertMethodCall(Object object, String method, Object param, Object expected) {
    431         Object value = InvokerHelper.invokeMethod(object, method, new Object[] { param });
    432         assertEquals("result of method: " + method, expected, value);
    433     }
    434
    435     /***
    436 	 * Asserts that invoking the method chooser finds the right overloaded
    437 	 * method implementation
    438 	 *
    439 	 * @param expected
    440 	 *            is the expected value of the method
    441 	 * @param arguments
    442 	 *            the argument(s) to the method invocation
    443 	 */
    444     protected void assertMethodChooser(Object expected, Object arguments) throws Throwable {
    445         Object value = invoke(this, "mockOverloadedMethod", arguments);
    446
    447         assertEquals("Invoking overloaded method for arguments: " + InvokerHelper.toString(arguments), expected, value);
    448     }
    449
    450     protected Object invoke(Object object, String method, Object args) throws Throwable {
    451         try {
    452             return invoker.invokeMethod(object, method, args);
    453         }
    454         catch (InvokerInvocationException e) {
    455             throw e.getCause();
    456         }
    457     }
    458 }