1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
package org.python.core;
import static org.junit.Assert.assertArrayEquals;
import junit.framework.TestCase;
/**
* Tests for ReflectedArgs, paying particular attention to the processing of arguments to match Java
* methods where the signature accepts varargs ({@code T...}) and a sequence type is passed. Recall
* that {@code T...} is just syntactic sugar for an array argument.
*
* <p>
* If a Java method that accepts varargs is called from Jython, the arguments are boxed into a
* {@code PyList} first. This should be skipped only if there are already the correct number of
* arguments and the final argument is a sequence type.
*
* It is not only the final argument that must be checked for being a sequence. Otherwise, we shall
* miss valid calls matching the underlying method.
*
* E.g. in Java<pre>
* void foo(Object... args) {}
* </pre>should be callable from Jython as<pre>
* foo(1, 2, [3, 4])
* </pre>where the list and the numbers are all objects and passed as a single array to the varargs
* method.
*
* <p>
* Similarly, if there are too few arguments, a java method such as<pre>
* void bar(int a, int[] b, int... c) {}
* </pre>should be callable from Jython as<pre>
* bar(1, [2, 3])
* </pre>where the vararg argument is empty and [2, 3] is passed as b.
*
* <p>
* The only remaining ambiguity is where a vararg parameter can take a sequence type as an element,
* e.g.<pre>
* void fizz(Object... args) {}
* </pre>calling from Jython as<pre>
* fizz([1,2,3])
* </pre>would be valid as either args being {@code [1,2,3]} or {@code [[1, 2, 3]]}. This ambiguity
* is also present in Java but behaves as if it was the former.
*/
public class ReflectedArgsTest extends TestCase {
/** Example methods for varargs processing. */
static class Demo {
public static void foo(Object... args) {}
public static void bar(int i, int[] j, int... rest) {}
public static void baz(Integer i, Integer[] j, Integer... rest) {}
}
/** {@code static void foo(Object... args)} */
private static final ReflectedArgs FOO_SIGNATURE =
new ReflectedArgs(null, new Class<?>[] {Object[].class}, Demo.class, true, true);
private static void fooCheck(Object[] args, Object... expected) {
// Expect one element that is an array of the expected values
assertEquals(1, args.length);
Object[] v = (Object[]) args[0];
assertEquals(expected.length, v.length);
for (int k = 0; k < expected.length; k++) {
assertEquals(expected[k], v[k]);
}
}
/** {@code static void bar(int i, int[] j, int... rest)} */
private static final ReflectedArgs BAR_SIGNATURE = new ReflectedArgs(null,
new Class<?>[] {int.class, int[].class, int[].class}, Demo.class, true, true);
private static void barCheck(Object[] args, int i, int[] j, int... rest) {
// Expect three elements conformant to the method arguments
assertEquals(3, args.length);
assertEquals(i, (int) args[0]);
assertArrayEquals(j, (int[]) args[1]);
assertArrayEquals(rest, (int[]) args[2]);
}
/** {@code static void baz(Integer i, Integer[] j, Integer... rest)} */
private static final ReflectedArgs BAZ_SIGNATURE = new ReflectedArgs(null,
new Class<?>[] {Integer.class, Integer[].class, Integer[].class}, Demo.class, true,
true);
private static void bazCheck(Object[] args, Integer i, Integer[] j, Integer... rest) {
// Expect three elements conformant to the method arguments
assertEquals(3, args.length);
assertEquals(i, (Integer) args[0]);
assertArrayEquals(j, (Integer[]) args[1]);
assertArrayEquals(rest, (Integer[]) args[2]);
}
/*
* If a function accepts Object Varargs (or Collection/array etc), the arguments should be boxed
* even if the final arg is a PySequenceList if the number of arguments not equal to the
* expected number of arguments.
*
* eg bar(1, [1, 2, 3]) should be valid for a call to void bar(int i, int[] j, int... rest) but
* should be boxed as bar(1, [1, 2, 3], [])
*/
/**
* Calling {@code foo(Object... args)} as Python {@code foo(3, 4, ["bar"])} calls Java
* {@code foo(3, 4, strlist)} where {@code strlist} is the representation of Python
* {@code ["bar"]}.
*/
public void testVarargsBoxedWithTooManyArgs() {
// calling foo(Object... args) as foo(3, 4, ["bar"])
PyList strlist = new PyList(new PyObject[] {Py.newString("bar")});
PyObject[] pyArgs = {Py.newInteger(3), Py.newInteger(4), strlist};
ReflectedCallData callData = new ReflectedCallData();
assertTrue(FOO_SIGNATURE.matches(null, pyArgs, Py.NoKeywords, callData));
fooCheck(callData.args, new Object[] {3, 4, strlist});
}
/**
* Calling {@code bar(int a, int[] b, int... c)} as Python {@code bar(1, [2, 3])} calls Java
* {@code bar(1, new int[]{2, 3})}.
*/
public void testPrimitiveVarargsBoxedWithTooFewArguments() {
// calling bar(int a, int[] b, int... c) as bar(1, [2, 3])
PyObject[] ints = {Py.newInteger(2), Py.newInteger(3)};
PyObject[] pyArgs = {Py.newInteger(1), new PyList(ints)};
ReflectedCallData callData = new ReflectedCallData();
assertTrue(BAR_SIGNATURE.matches(null, pyArgs, Py.NoKeywords, callData));
barCheck(callData.args, 1, new int[] {2, 3});
}
/**
* Calling {@code baz(Integer a, Integer[] b, Integer... c)} as Python {@code baz(1, [2, 3])}
* calls Java {@code baz(1, new Integer[]{2, 3})}.
*/
public void testVarargsBoxedWithTooFewArguments() {
// calling baz(Integer a, Integer[] b, Integer... c) as bar(1, [2, 3])
PyObject[] ints = {Py.newInteger(2), Py.newInteger(3)};
PyObject[] pyArgs = {Py.newInteger(1), new PyList(ints)};
ReflectedCallData callData = new ReflectedCallData();
assertTrue(BAZ_SIGNATURE.matches(null, pyArgs, Py.NoKeywords, callData));
bazCheck(callData.args, 1, new Integer[] {2, 3});
}
/**
* Calling {@code foo(Object... args)} as Python {@code foo([1,2,3])} calls Java {@code foo(new
* Object[]{1,2,3})}.
*/
public void testVarargsNotBoxedWithCorrectArgs() {
// calling foo(Object... args) as foo([1,2,3])
PyObject[] ints = {Py.newInteger(1), Py.newInteger(2), Py.newInteger(3)};
PyObject[] pyArgs = {new PyList(ints)};
ReflectedCallData callData = new ReflectedCallData();
assertTrue(FOO_SIGNATURE.matches(null, pyArgs, Py.NoKeywords, callData));
fooCheck(callData.args, 1, 2, 3);
}
/**
* Calling {@code foo(Object... args)} as Python {@code foo("foo", "bar")} calls Java
* {@code foo("foo", "bar")}.
*/
public void testVarargsBoxedWithNoSequences() {
// calling foo(Object... args) as foo("foo", "bar")
PyObject[] pyArgs = {Py.newString("foo"), Py.newString("bar")};
ReflectedCallData callData = new ReflectedCallData();
assertTrue(FOO_SIGNATURE.matches(null, pyArgs, Py.NoKeywords, callData));
fooCheck(callData.args, "foo", "bar");
}
}
|