    1   /*
    2    * $Id: InvokeMethodTest.java 4099 2006-10-10 18:06:52Z blackdrag $
    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 org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
    52
    53  import junit.framework.AssertionFailedError;
    54
    55  /***
    56   * Tests method invocation
    57   *
    58   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
    59   * @version $Revision: 4099 $
    60   */
    61  public class InvokeMethodTest extends GroovyTestCase {
    62
    63      protected Invoker invoker = InvokerHelper.getInstance();
    64
    65      // Method invocation tests
    66      //-------------------------------------------------------------------------
    67
    68      public void testInvokeMethodNoParams() throws Throwable {
    69          Object value = invoke(this, "mockCallWithNoParams", null);
    70          assertEquals("return value", "NoParams", value);
    71
    72          value = invoke(this, "mockCallWithNoParams", new Object[0]);
    73          assertEquals("return value", "NoParams", value);
    74      }
    75
    76      public void testInvokeMethodOneParam() throws Throwable {
    77          Object value = invoke(this, "mockCallWithOneParam", "abc");
    78          assertEquals("return value", "OneParam", value);
    79      }
    80
    81      public void testInvokeMethodOneParamWhichIsNull() throws Throwable {
    82          Object value = invoke(this, "mockCallWithOneNullParam", new Object[] { null });
    83          assertEquals("return value", "OneParamWithNull", value);
    84
    85          value = invoke(this, "mockCallWithOneNullParam", null);
    86          assertEquals("return value", "OneParamWithNull", value);
    87      }
    88
    89      public void testInvokeOverloadedMethodWithOneParamWhichIsNull() throws Throwable {
    90          Object value = invoke(this, "mockOverloadedMethod", new Object[] { null });
    91          assertEquals("return value", "Object", value);
    92      }
    93
    94      public void testInvokeMethodOneCollectionParameter() throws Throwable {
    95          Object[] foo = { "a", "b", "c" };
    96
    97          Object value = invoke(this, "mockCallWithOneCollectionParam", new Object[] { foo });
    98          assertEquals("return value", new Integer(3), value);
    99
    100         List list = new ArrayList();
    101         list.add("a");
    102         list.add("b");
    103         value = invoke(this, "mockCallWithOneCollectionParam", list);
    104         assertEquals("return value", new Integer(2), value);
    105     }
    106
    107     public void testInvokePrintlnMethod() throws Throwable {
    108         Object value = invoke(System.out, "println", "testing System.out.println...");
    109         assertEquals("return value", null, value);
    110     }
    111
    112     public void testMethodChooserNull() throws Throwable {
    113         assertMethodChooser("Object", new Object[] { null });
    114     }
    115
    116     public void testMethodChooserNoParams() throws Throwable {
    117         assertMethodChooser("void", null);
    118     }
    119
    120     public void testMethodChooserObject() throws Throwable {
    121         assertMethodChooser("Object", new Object());
    122         assertMethodChooser("Object", new Date());
    123     }
    124
    125     public void testMethodChooserString_FAILS() throws Throwable { if (notYetImplemented()) return;
    126         assertMethodChooser("String", "foo");
    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         try {
    195             invoke(this, "fail", "hello");
    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         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             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             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 = DefaultTypeTransformation.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 }