/* Copyright (c) 2007 Timothy Wall, All Rights Reserved
*
* The contents of this file is dual-licensed under 2
* alternative Open Source/Free licenses: LGPL 2.1 or later and
* Apache License 2.0. (starting with JNA version 4.0.0).
*
* You can freely decide which license you want to apply to
* the project.
*
* You may obtain a copy of the LGPL License at:
*
* http://www.gnu.org/licenses/licenses.html
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "LGPL2.1".
*
* You may obtain a copy of the Apache License at:
*
* http://www.apache.org/licenses/
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "AL2.0".
*/
package com.sun.jna;
import java.util.Arrays;
import java.util.List;
import junit.framework.TestCase;
import com.sun.jna.ArgumentsMarshalTest.TestLibrary.CheckFieldAlignment;
/** Exercise a range of native methods.
*
* @author twall@users.sf.net
*/
//@SuppressWarnings("unused")
public class ArgumentsMarshalTest extends TestCase {
private static final String UNICODE = "[\0444]";
public static interface TestLibrary extends Library {
class CheckFieldAlignment extends Structure {
public static class ByValue extends CheckFieldAlignment
implements Structure.ByValue { }
public static class ByReference extends CheckFieldAlignment
implements Structure.ByReference { }
public byte int8Field;
public short int16Field;
public int int32Field;
public long int64Field;
public float floatField;
public double doubleField;
@Override
public List<String> getFieldOrder() {
return Arrays.asList(new String[] { "int8Field", "int16Field", "int32Field", "int64Field", "floatField", "doubleField" });
}
public CheckFieldAlignment() {
int8Field = (byte)fieldOffset("int8Field");
int16Field = (short)fieldOffset("int16Field");
int32Field = fieldOffset("int32Field");
int64Field = fieldOffset("int64Field");
floatField = fieldOffset("floatField");
doubleField = fieldOffset("doubleField");
}
}
String returnStringArgument(Object arg);
boolean returnBooleanArgument(boolean arg);
byte returnInt8Argument(byte arg);
char returnWideCharArgument(char arg);
short returnInt16Argument(short arg);
int returnInt32Argument(int i);
long returnInt64Argument(long l);
NativeLong returnLongArgument(NativeLong l);
float returnFloatArgument(float f);
double returnDoubleArgument(double d);
String returnStringArgument(String s);
WString returnWStringArgument(WString s);
Pointer returnPointerArgument(Pointer p);
String returnStringArrayElement(String[] args, int which);
WString returnWideStringArrayElement(WString[] args, int which);
Pointer returnPointerArrayElement(Pointer[] args, int which);
public static class TestPointerType extends PointerType {
public TestPointerType() { }
public TestPointerType(Pointer p) { super(p); }
}
TestPointerType returnPointerArrayElement(TestPointerType[] args, int which);
CheckFieldAlignment returnPointerArrayElement(CheckFieldAlignment.ByReference[] args, int which);
int returnRotatedArgumentCount(String[] args);
long checkInt64ArgumentAlignment(int i, long j, int i2, long j2);
double checkDoubleArgumentAlignment(float i, double j, float i2, double j2);
Pointer testStructurePointerArgument(CheckFieldAlignment p);
int testStructureByValueArgument(CheckFieldAlignment.ByValue p);
int testStructureArrayInitialization(CheckFieldAlignment[] p, int len);
int testStructureByReferenceArrayInitialization(CheckFieldAlignment.ByReference[] p, int len);
void modifyStructureArray(CheckFieldAlignment[] p, int length);
void modifyStructureByReferenceArray(CheckFieldAlignment.ByReference[] p, int length);
int fillInt8Buffer(byte[] buf, int len, byte value);
int fillInt16Buffer(short[] buf, int len, short value);
int fillInt32Buffer(int[] buf, int len, int value);
int fillInt64Buffer(long[] buf, int len, long value);
int fillFloatBuffer(float[] buf, int len, float value);
int fillDoubleBuffer(double[] buf, int len, double value);
// Nonexistent functions
boolean returnBooleanArgument(Object arg);
// Structure
class MinTestStructure extends Structure {
public int field;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "field" });
}
}
Pointer testStructurePointerArgument(MinTestStructure s);
class VariableSizedStructure extends Structure {
public int length;
public byte[] buffer;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "length", "buffer" });
}
public VariableSizedStructure(String arg) {
length = arg.length() + 1;
buffer = new byte[length];
System.arraycopy(arg.getBytes(), 0, buffer, 0, arg.length());
}
}
String returnStringFromVariableSizedStructure(VariableSizedStructure s);
class CbStruct extends Structure {
public static interface TestCallback extends Callback {
int callback(int arg1, int arg2);
}
public TestCallback cb;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "cb" });
}
}
void setCallbackInStruct(CbStruct cbstruct);
}
TestLibrary lib;
@Override
protected void setUp() {
lib = Native.loadLibrary("testlib", TestLibrary.class);
}
@Override
protected void tearDown() {
lib = null;
}
public void testJavaObjectArgument() {
Object o = this;
try {
lib.returnStringArgument(o);
fail("Java Object arguments should throw IllegalArgumentException");
}
catch(IllegalArgumentException e) {
assertTrue("Exception should include Object type description: " + e,
e.getMessage().indexOf(o.getClass().getName()) != -1);
}
catch(Throwable e) {
fail("Java Object arguments should throw IllegalArgumentException, not " + e);
}
}
public void testBooleanArgument() {
assertTrue("True argument should be returned",
lib.returnBooleanArgument(true));
assertFalse("False argument should be returned",
lib.returnBooleanArgument(false));
}
public void testInt8Argument() {
byte b = 0;
assertEquals("Wrong value returned",
b, lib.returnInt8Argument(b));
b = 127;
assertEquals("Wrong value returned",
b, lib.returnInt8Argument(b));
b = -128;
assertEquals("Wrong value returned",
b, lib.returnInt8Argument(b));
}
public void testWideCharArgument() {
char c = 0;
assertEquals("Wrong value returned",
c, lib.returnWideCharArgument(c));
c = 0xFFFF;
assertEquals("Wrong value returned",
c, lib.returnWideCharArgument(c));
c = 0x7FFF;
assertEquals("Wrong value returned",
c, lib.returnWideCharArgument(c));
}
public void testInt16Argument() {
short v = 0;
assertEquals("Wrong value returned",
v, lib.returnInt16Argument(v));
v = 32767;
assertEquals("Wrong value returned",
v, lib.returnInt16Argument(v));
v = -32768;
assertEquals("Wrong value returned",
v, lib.returnInt16Argument(v));
}
public void testIntArgument() {
int value = 0;
assertEquals("Should return 32-bit argument",
value, lib.returnInt32Argument(value));
value = 1;
assertEquals("Should return 32-bit argument",
value, lib.returnInt32Argument(value));
value = 0x7FFFFFFF;
assertEquals("Should return 32-bit argument",
value, lib.returnInt32Argument(value));
value = 0x80000000;
assertEquals("Should return 32-bit argument",
value, lib.returnInt32Argument(value));
}
public void testLongArgument() {
long value = 0L;
assertEquals("Should return 64-bit argument",
value, lib.returnInt64Argument(value));
value = 1L;
assertEquals("Should return 64-bit argument",
value, lib.returnInt64Argument(value));
value = 0x7FFFFFFFL;
assertEquals("Should return 64-bit argument",
value, lib.returnInt64Argument(value));
value = 0x80000000L;
assertEquals("Should return 64-bit argument",
value, lib.returnInt64Argument(value));
value = 0x7FFFFFFF00000000L;
assertEquals("Should return 64-bit argument",
value, lib.returnInt64Argument(value));
value = 0x8000000000000000L;
assertEquals("Should return 64-bit argument",
value, lib.returnInt64Argument(value));
}
public void testNativeLongArgument() {
NativeLong value = new NativeLong(0);
assertEquals("Should return 0",
value, lib.returnLongArgument(value));
value = new NativeLong(1);
assertEquals("Should return 1",
value, lib.returnLongArgument(value));
value = new NativeLong(0x7FFFFFFF);
assertEquals("Should return 0x7FFFFFFF",
value, lib.returnLongArgument(value));
value = new NativeLong(0x80000000);
assertEquals("Should return 0x80000000",
value, lib.returnLongArgument(value));
}
public interface NativeMappedLibrary extends Library {
int returnInt32Argument(Custom arg);
int returnInt32Argument(size_t arg);
long returnInt64Argument(size_t arg);
}
public static class size_t extends IntegerType {
private static final long serialVersionUID = 1L;
public size_t() {
this(0);
}
public size_t(long value) {
super(Native.SIZE_T_SIZE, true);
setValue(value);
}
}
public static class Custom implements NativeMapped {
private int value;
public Custom() { }
public Custom(int value) {
this.value = value;
}
@Override
public Object fromNative(Object nativeValue, FromNativeContext context) {
return new Custom(((Integer)nativeValue).intValue());
}
@Override
public Class<?> nativeType() {
return Integer.class;
}
@Override
public Object toNative() {
return Integer.valueOf(value);
}
}
protected NativeMappedLibrary loadNativeMappedLibrary() {
return Native.loadLibrary("testlib", NativeMappedLibrary.class);
}
public void testNativeMappedArgument() {
NativeMappedLibrary lib = loadNativeMappedLibrary();
final int MAGIC = 0x12345678;
Custom arg = new Custom(MAGIC);
assertEquals("Argument not mapped", MAGIC, lib.returnInt32Argument(arg));
if (Native.SIZE_T_SIZE == 4) {
size_t size = new size_t(MAGIC);
assertEquals("Argument not mapped", MAGIC, lib.returnInt32Argument(size));
}
else {
final long MAGIC64 = 0x123456789ABCDEFL;
size_t size = new size_t(MAGIC64);
assertEquals("Argument not mapped", MAGIC64, lib.returnInt64Argument(size));
}
}
public void testPointerArgumentReturn() {
assertEquals("Expect null pointer",
null, lib.returnPointerArgument(null));
Structure s = new TestLibrary.CheckFieldAlignment();
assertEquals("Expect structure pointer",
s.getPointer(),
lib.returnPointerArgument(s.getPointer()));
}
static final String MAGIC = "magic" + UNICODE;
public void testStringArgumentReturn() {
assertEquals("Expect null pointer", null, lib.returnStringArgument(null));
assertEquals("Expect string magic", MAGIC, lib.returnStringArgument(MAGIC));
}
static final WString WMAGIC = new WString("magic" + UNICODE);
public void testWStringArgumentReturn() {
assertEquals("Expect null pointer", null, lib.returnWStringArgument(null));
assertEquals("Expect string magic", WMAGIC.toString(), lib.returnWStringArgument(WMAGIC).toString());
}
public void testInt64ArgumentAlignment() {
long value = lib.checkInt64ArgumentAlignment(0x10101010, 0x1111111111111111L,
0x01010101, 0x2222222222222222L);
assertEquals("Improper handling of interspersed int32/int64",
0x3333333344444444L, value);
}
public void testDoubleArgumentAlignment() {
double value = lib.checkDoubleArgumentAlignment(1f, 2d, 3f, 4d);
assertEquals("Improper handling of interspersed float/double",
10d, value, 0);
}
public void testStructurePointerArgument() {
TestLibrary.CheckFieldAlignment struct = new TestLibrary.CheckFieldAlignment();
assertEquals("Native address of structure should be returned",
struct.getPointer(),
lib.testStructurePointerArgument(struct));
// ensure that even if the argument is ByValue, it's passed as ptr
struct = new TestLibrary.CheckFieldAlignment.ByValue();
assertEquals("Structure argument should be passed according to method "
+ "parameter type, not argument type",
struct.getPointer(),
lib.testStructurePointerArgument(struct));
struct = null;
assertNull("Null argument should be returned",
lib.testStructurePointerArgument(struct));
}
public void testStructureByValueArgument() {
TestLibrary.CheckFieldAlignment.ByValue struct =
new TestLibrary.CheckFieldAlignment.ByValue();
assertEquals("Wrong alignment in " + struct.toString(true),
"0", Integer.toHexString(lib.testStructureByValueArgument(struct)));
}
public void testStructureByValueTypeInfo() {
class TestStructure extends Structure implements Structure.ByValue {
public byte b;
public char c;
public short s;
public int i;
public long j;
public float f;
public double d;
public Pointer[] parray = new Pointer[2];
public byte[] barray = new byte[2];
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "b", "c", "s", "i", "j", "f", "d", "parray", "barray" });
}
}
Structure s = new TestStructure();
// Force generation of type info
s.size();
}
public void testWriteStructureArrayArgumentMemory() {
final int LENGTH = 10;
TestLibrary.CheckFieldAlignment block = new TestLibrary.CheckFieldAlignment();
TestLibrary.CheckFieldAlignment[] array =
(TestLibrary.CheckFieldAlignment[])block.toArray(LENGTH);
for (int i=0;i < array.length;i++) {
array[i].int32Field = i;
}
assertEquals("Structure array memory not properly initialized",
-1, lib.testStructureArrayInitialization(array, array.length));
}
public void testUninitializedStructureArrayArgument() {
final int LENGTH = 10;
TestLibrary.CheckFieldAlignment[] block =
new TestLibrary.CheckFieldAlignment[LENGTH];
lib.modifyStructureArray(block, block.length);
for (int i=0;i < block.length;i++) {
assertNotNull("Structure array not initialized at " + i, block[i]);
assertEquals("Wrong value for int32 field of structure at " + i,
i, block[i].int32Field);
assertEquals("Wrong value for int64 field of structure at " + i,
i + 1, block[i].int64Field);
assertEquals("Wrong value for float field of structure at " + i,
i + 2, block[i].floatField, 0);
assertEquals("Wrong value for double field of structure at " + i,
i + 3, block[i].doubleField, 0);
}
}
public void testRejectNoncontiguousStructureArrayArgument() {
TestLibrary.CheckFieldAlignment s1, s2, s3;
s3 = new TestLibrary.CheckFieldAlignment();
s1 = new TestLibrary.CheckFieldAlignment();
s2 = new TestLibrary.CheckFieldAlignment();
TestLibrary.CheckFieldAlignment[] block = { s1, s2, s3 };
try {
lib.modifyStructureArray(block, block.length);
fail("Library invocation should fail");
}
catch(IllegalArgumentException e) {
}
}
public void testRejectIncompatibleStructureArrayArgument() {
TestLibrary.CheckFieldAlignment s1 = new TestLibrary.CheckFieldAlignment.ByReference();
TestLibrary.CheckFieldAlignment[] autoArray = (TestLibrary.CheckFieldAlignment[])s1.toArray(3);
try {
lib.modifyStructureArray(autoArray, autoArray.length);
}
catch(IllegalArgumentException e) {
}
TestLibrary.CheckFieldAlignment.ByReference[] byRefArray =
(TestLibrary.CheckFieldAlignment.ByReference[])s1.toArray(3);
try {
lib.modifyStructureArray(byRefArray, byRefArray.length);
}
catch(IllegalArgumentException e) {
}
TestLibrary.CheckFieldAlignment[] arrayWithRefElements = { autoArray[0], autoArray[1], autoArray[2] };
try {
lib.modifyStructureArray(arrayWithRefElements, arrayWithRefElements.length);
}
catch(IllegalArgumentException e) {
}
}
/** When passing an array of <code>struct*</code> to native, be sure to
invoke <code>Structure.write()</code> on each of the elements. */
public void testWriteStructureByReferenceArrayArgumentMemory() {
TestLibrary.CheckFieldAlignment.ByReference[] array = {
new TestLibrary.CheckFieldAlignment.ByReference(),
new TestLibrary.CheckFieldAlignment.ByReference(),
new TestLibrary.CheckFieldAlignment.ByReference(),
new TestLibrary.CheckFieldAlignment.ByReference(),
};
for (int i=0;i < array.length;i++) {
array[i].int32Field = i;
}
assertEquals("Structure.ByReference array memory not properly initialized",
-1, lib.testStructureByReferenceArrayInitialization(array, array.length));
}
public void testReadStructureByReferenceArrayArgumentMemory() {
TestLibrary.CheckFieldAlignment.ByReference[] array = {
new TestLibrary.CheckFieldAlignment.ByReference(),
new TestLibrary.CheckFieldAlignment.ByReference(),
new TestLibrary.CheckFieldAlignment.ByReference(),
new TestLibrary.CheckFieldAlignment.ByReference(),
};
lib.modifyStructureByReferenceArray(array, array.length);
for (int i=0;i < array.length;i++) {
assertEquals("Wrong value for int32 field of structure at " + i,
i, array[i].int32Field);
assertEquals("Wrong value for int64 field of structure at " + i,
i + 1, array[i].int64Field);
assertEquals("Wrong value for float field of structure at " + i,
i + 2, array[i].floatField, 0);
assertEquals("Wrong value for double field of structure at " + i,
i + 3, array[i].doubleField, 0);
}
}
public void testByteArrayArgument() {
byte[] buf = new byte[1024];
final byte MAGIC = (byte)0xED;
assertEquals("Wrong return value", buf.length,
lib.fillInt8Buffer(buf, buf.length, MAGIC));
for (int i=0;i < buf.length;i++) {
assertEquals("Bad value at index " + i, MAGIC, buf[i]);
}
}
public void testShortArrayArgument() {
short[] buf = new short[1024];
final short MAGIC = (short)0xABED;
assertEquals("Wrong return value", buf.length,
lib.fillInt16Buffer(buf, buf.length, MAGIC));
for (int i=0;i < buf.length;i++) {
assertEquals("Bad value at index " + i, MAGIC, buf[i]);
}
}
public void testIntArrayArgument() {
int[] buf = new int[1024];
final int MAGIC = 0xABEDCF23;
assertEquals("Wrong return value", buf.length,
lib.fillInt32Buffer(buf, buf.length, MAGIC));
for (int i=0;i < buf.length;i++) {
assertEquals("Bad value at index " + i, MAGIC, buf[i]);
}
}
public void testLongArrayArgument() {
long[] buf = new long[1024];
final long MAGIC = 0x1234567887654321L;
assertEquals("Wrong return value", buf.length,
lib.fillInt64Buffer(buf, buf.length, MAGIC));
for (int i=0;i < buf.length;i++) {
assertEquals("Bad value at index " + i, MAGIC, buf[i]);
}
}
public void testUnsupportedJavaObjectArgument() {
try {
lib.returnBooleanArgument(this);
fail("Unsupported Java objects should be rejected");
}
catch(IllegalArgumentException e) {
}
}
public void testStringArrayArgument() {
String[] args = { "one"+UNICODE, "two"+UNICODE, "three"+UNICODE };
assertEquals("Wrong value returned", args[0], lib.returnStringArrayElement(args, 0));
assertNull("Native String array should be null terminated",
lib.returnStringArrayElement(args, args.length));
}
public void testWideStringArrayArgument() {
WString[] args = { new WString("one"+UNICODE), new WString("two"+UNICODE), new WString("three"+UNICODE) };
assertEquals("Wrong value returned", args[0], lib.returnWideStringArrayElement(args, 0));
assertNull("Native WString array should be null terminated",
lib.returnWideStringArrayElement(args, args.length));
}
public void testPointerArrayArgument() {
Pointer[] args = {
new NativeString(getName()).getPointer(),
null,
new NativeString(getName()+"2").getPointer(),
};
Pointer[] originals = new Pointer[args.length];
System.arraycopy(args, 0, originals, 0, args.length);
assertEquals("Wrong value returned", args[0], lib.returnPointerArrayElement(args, 0));
assertNull("Wrong value returned", lib.returnPointerArrayElement(args, 1));
assertEquals("Wrong value returned", args[2], lib.returnPointerArrayElement(args, 2));
assertNull("Native array should be null terminated", lib.returnPointerArrayElement(args, 3));
assertSame("Argument pointers should remain unmodified [0]",
originals[0], args[0]);
assertSame("Argument pointers should remain unmodified [2]",
originals[2], args[2]);
}
public void testNativeMappedArrayArgument() {
TestLibrary.TestPointerType[] args = {
new TestLibrary.TestPointerType(new NativeString(getName()).getPointer()),
null,
new TestLibrary.TestPointerType(new NativeString(getName()+"2").getPointer()),
};
assertEquals("Wrong value returned", args[0], lib.returnPointerArrayElement(args, 0));
assertNull("Wrong value returned", lib.returnPointerArrayElement(args, 1));
assertEquals("Wrong value returned", args[2], lib.returnPointerArrayElement(args, 2));
};
public void testStructureByReferenceArrayArgument() {
CheckFieldAlignment.ByReference[] args = {
new CheckFieldAlignment.ByReference(),
null,
new CheckFieldAlignment.ByReference(),
};
assertTrue("Wrong value returned (0)", args[0].dataEquals(lib.returnPointerArrayElement(args, 0), true));
assertNull("Wrong value returned (1)", lib.returnPointerArrayElement(args, 1));
assertTrue("Wrong value returned (2)", args[2].dataEquals(lib.returnPointerArrayElement(args, 2), true));
assertNull("Native array should be null terminated", lib.returnPointerArrayElement(args, 3));
}
public void testModifiedCharArrayArgument() {
String[] args = { "one", "two", "three" };
assertEquals("Wrong native array count", args.length, lib.returnRotatedArgumentCount(args));
assertEquals("Modified array argument not re-read",
Arrays.asList(new String[] { "two", "three", "one" }),
Arrays.asList(args));
}
public void testReadFunctionPointerAsCallback() {
TestLibrary.CbStruct s = new TestLibrary.CbStruct();
assertNull("Function pointer field should be null", s.cb);
lib.setCallbackInStruct(s);
assertNotNull("Callback field not set", s.cb);
}
public void testCallProxiedFunctionPointer() {
TestLibrary.CbStruct s = new TestLibrary.CbStruct();
lib.setCallbackInStruct(s);
assertEquals("Proxy to native function pointer failed: " + s.cb,
3, s.cb.callback(1, 2));
}
public void testVariableSizedStructureArgument() {
String EXPECTED = getName();
TestLibrary.VariableSizedStructure s =
new TestLibrary.VariableSizedStructure(EXPECTED);
assertEquals("Wrong string returned from variable sized struct",
EXPECTED, lib.returnStringFromVariableSizedStructure(s));
}
public void testDisableAutoSynch() {
TestLibrary.MinTestStructure s = new TestLibrary.MinTestStructure();
final int VALUE = 42;
s.field = VALUE;
s.setAutoWrite(false);
lib.testStructurePointerArgument(s);
assertEquals("Auto write should be disabled", 0, s.field);
final int EXPECTED = s.field;
s.getPointer().setInt(0, VALUE);
s.setAutoRead(false);
lib.testStructurePointerArgument(s);
assertEquals("Auto read should be disabled", EXPECTED, s.field);
}
public static void main(java.lang.String[] argList) {
junit.textui.TestRunner.run(ArgumentsMarshalTest.class);
}
}