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 }