/* Copyright (c) 2007 Timothy Wall, All Rights Reserved * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package com.sun.jna; import java.util.HashMap; import java.util.Map; import java.util.Properties; import junit.framework.TestCase; public class NativeTest extends TestCase { public void testDefaultStringEncoding() throws Exception { String encoding = System.getProperty("file.encoding"); // Keep stuff within the extended ASCII range so we work with more // limited native encodings String unicode = "Un \u00e9l\u00e9ment gr\u00e2ce \u00e0 l'index"; if (!unicode.equals(new String(unicode.getBytes()))) { // If the extended characters aren't encodable in the default // encoding, punt and use straight ASCII unicode = ""; for (char ch=1;ch < 128;ch++) { unicode += ch; } } String unicodez = unicode + "\0more stuff"; byte[] defaultEncoded = Native.getBytes(unicode); byte[] expected = unicode.getBytes(); for (int i=0;i < Math.min(defaultEncoded.length, expected.length);i++) { assertEquals("Improperly encoded (" + encoding + ") from Java at " + i, expected[i], defaultEncoded[i]); } assertEquals("Wrong number of encoded characters (" + encoding + ")", expected.length, defaultEncoded.length); String result = Native.toString(defaultEncoded); assertEquals("Improperly decoded from native bytes (" + encoding + ")", unicode, result); assertEquals("Should truncate bytes at NUL terminator", unicode, Native.toString(unicodez.getBytes())); } public void testCustomStringEncoding() throws Exception { Properties oldprops = (Properties)System.getProperties().clone(); try { String encoding = "UTF8"; System.setProperty("jna.encoding", encoding); String unicode = "\u0444\u043b\u0441\u0432\u0443"; String unicodez = unicode + "\0more stuff"; byte[] utf8 = Native.getBytes(unicode); byte[] expected = unicode.getBytes(encoding); for (int i=0;i < Math.min(utf8.length, expected.length);i++) { assertEquals("Improperly encoded at " + i, expected[i], utf8[i]); } assertEquals("Wrong number of encoded characters", expected.length, utf8.length); String result = Native.toString(utf8); assertEquals("Improperly decoded", unicode, result); assertEquals("Should truncate bytes at NUL terminator", unicode, Native.toString(unicodez.getBytes(encoding))); } finally { System.setProperties(oldprops); } } public static interface TestLib extends Library { interface VoidCallback extends Callback { void callback(); } void callVoidCallback(VoidCallback callback); } public void testSynchronizedAccess() throws Exception { final boolean[] lockHeld = { false }; final NativeLibrary nlib = NativeLibrary.getInstance("testlib"); final TestLib lib = (TestLib)Native.loadLibrary("testlib", TestLib.class); final TestLib synchlib = (TestLib)Native.synchronizedLibrary(lib); final TestLib.VoidCallback cb = new TestLib.VoidCallback() { public void callback() { lockHeld[0] = Thread.holdsLock(nlib); } }; Thread t0 = new Thread() { public void run() { lib.callVoidCallback(cb); } }; t0.start(); t0.join(); assertFalse("NativeLibrary lock should not be held during native call to normal library", lockHeld[0]); Thread t1 = new Thread() { public void run() { synchlib.callVoidCallback(cb); } }; t1.start(); t1.join(); assertTrue("NativeLibrary lock should be held during native call to synchronized library", lockHeld[0]); } interface TestInterface extends Library { static class InnerTestClass extends Structure { interface TestCallback extends Callback { } static class InnerSubclass extends InnerTestClass implements Structure.ByReference { } } } public void testFindInterfaceClass() throws Exception { Class interfaceClass = TestInterface.class; Class cls = TestInterface.InnerTestClass.class; Class subClass = TestInterface.InnerTestClass.InnerSubclass.class; Class callbackClass = TestInterface.InnerTestClass.TestCallback.class; assertEquals("Enclosing interface not found for class", interfaceClass, Native.findEnclosingLibraryClass(cls)); assertEquals("Enclosing interface not found for derived class", interfaceClass, Native.findEnclosingLibraryClass(subClass)); assertEquals("Enclosing interface not found for callback", interfaceClass, Native.findEnclosingLibraryClass(callbackClass)); } public interface TestInterfaceWithInstance extends Library { int TEST_ALIGNMENT = Structure.ALIGN_NONE; TypeMapper TEST_MAPPER = new DefaultTypeMapper(); Map TEST_OPTS = new HashMap() { { put(OPTION_TYPE_MAPPER, TEST_MAPPER); put(OPTION_STRUCTURE_ALIGNMENT, new Integer(TEST_ALIGNMENT)); }}; TestInterfaceWithInstance ARBITRARY = (TestInterfaceWithInstance) Native.loadLibrary("testlib", TestInterfaceWithInstance.class, TEST_OPTS); } public void testOptionsInferenceFromInstanceField() { assertEquals("Wrong options found for interface which provides an instance", TestInterfaceWithInstance.TEST_OPTS, Native.getLibraryOptions(TestInterfaceWithInstance.class)); assertEquals("Wrong type mapper found", TestInterfaceWithInstance.TEST_MAPPER, Native.getTypeMapper(TestInterfaceWithInstance.class)); assertEquals("Wrong alignment found", TestInterfaceWithInstance.TEST_ALIGNMENT, Native.getStructureAlignment(TestInterfaceWithInstance.class)); } public interface TestInterfaceWithOptions extends Library { int TEST_ALIGNMENT = Structure.ALIGN_NONE; TypeMapper TEST_MAPPER = new DefaultTypeMapper(); Map OPTIONS = new HashMap() { { put(OPTION_TYPE_MAPPER, TEST_MAPPER); put(OPTION_STRUCTURE_ALIGNMENT, new Integer(TEST_ALIGNMENT)); }}; } public void testOptionsInferenceFromOptionsField() { assertEquals("Wrong options found for interface which provides OPTIONS", TestInterfaceWithOptions.OPTIONS, Native.getLibraryOptions(TestInterfaceWithOptions.class)); assertEquals("Wrong type mapper found", TestInterfaceWithOptions.TEST_MAPPER, Native.getTypeMapper(TestInterfaceWithOptions.class)); assertEquals("Wrong alignment found", TestInterfaceWithOptions.TEST_ALIGNMENT, Native.getStructureAlignment(TestInterfaceWithOptions.class)); } public interface TestInterfaceWithTypeMapper extends Library { TypeMapper TEST_MAPPER = new DefaultTypeMapper(); TypeMapper TYPE_MAPPER = TEST_MAPPER; } public void testOptionsInferenceFromTypeMapperField() { assertEquals("Wrong type mapper found for interface which provides TYPE_MAPPER", TestInterfaceWithTypeMapper.TEST_MAPPER, Native.getTypeMapper(TestInterfaceWithTypeMapper.class)); } public interface TestInterfaceWithAlignment extends Library { int STRUCTURE_ALIGNMENT = Structure.ALIGN_NONE; } public void testOptionsInferenceFromAlignmentField() { assertEquals("Wrong alignment found for interface which provides STRUCTURE_ALIGNMENT", Structure.ALIGN_NONE, Native.getStructureAlignment(TestInterfaceWithAlignment.class)); } public void testCharArrayToString() { char[] buf = { 'a', 'b', 'c', '\0', 'd', 'e' }; assertEquals("Wrong String generated", "abc", Native.toString(buf)); } public void testByteArrayToString() { byte[] buf = { 'a', 'b', 'c', '\0', 'd', 'e' }; assertEquals("Wrong String generated", "abc", Native.toString(buf)); } public void testToByteArray() { final String VALUE = getName(); byte[] buf = Native.toByteArray(VALUE); assertEquals("Wrong byte array length", VALUE.length()+1, buf.length); assertEquals("Missing NUL terminator", (byte)0, buf[buf.length-1]); assertEquals("Wrong byte array contents", VALUE, new String(buf, 0, buf.length-1)); } public void testToCharArray() { final String VALUE = getName(); char[] buf = Native.toCharArray(VALUE); assertEquals("Wrong char array length", VALUE.length()+1, buf.length); assertEquals("Missing NUL terminator", (char)0, buf[buf.length-1]); assertEquals("Wrong char array contents: " + new String(buf), VALUE, new String(buf, 0, buf.length-1)); } public void testOSPrefix() { assertEquals("Wrong resource path", "/com/sun/jna/win32-x86", Native.getNativeLibraryResourcePath(Platform.WINDOWS, "x86", "Windows")); assertEquals("Wrong resource path Windows/i386", "/com/sun/jna/win32-x86", Native.getNativeLibraryResourcePath(Platform.WINDOWS, "i386", "Windows")); assertEquals("Wrong resource path Mac/x86", "/com/sun/jna/darwin", Native.getNativeLibraryResourcePath(Platform.MAC, "x86", "Darwin")); assertEquals("Wrong resource path Mac/x86_64", "/com/sun/jna/darwin", Native.getNativeLibraryResourcePath(Platform.MAC, "x86_64", "Mac")); assertEquals("Wrong resource path Solaris/sparc", "/com/sun/jna/sunos-sparc", Native.getNativeLibraryResourcePath(Platform.SOLARIS, "sparc", "Solaris")); assertEquals("Wrong resource path SunOS/sparcv9", "/com/sun/jna/sunos-sparcv9", Native.getNativeLibraryResourcePath(Platform.SOLARIS, "sparcv9", "SunOS")); assertEquals("Wrong resource path Linux/i386", "/com/sun/jna/linux-i386", Native.getNativeLibraryResourcePath(Platform.LINUX, "i386", "Linux/Gnu")); assertEquals("Wrong resource path Linux/x86", "/com/sun/jna/linux-i386", Native.getNativeLibraryResourcePath(Platform.LINUX, "x86", "Linux")); assertEquals("Wrong resource path OpenBSD/x86", "/com/sun/jna/openbsd-i386", Native.getNativeLibraryResourcePath(Platform.OPENBSD, "x86", "OpenBSD")); assertEquals("Wrong resource path FreeBSD/x86", "/com/sun/jna/freebsd-i386", Native.getNativeLibraryResourcePath(Platform.FREEBSD, "x86", "FreeBSD")); assertEquals("Wrong resource path other/other", "/com/sun/jna/name-ppc", Native.getNativeLibraryResourcePath(Platform.UNSPECIFIED, "PowerPC", "Name Of System")); } public static class DirectMapping { public static class DirectStructure extends Structure { public int field; } public static interface DirectCallback extends Callback { void invoke(); } public DirectMapping(Map options) { Native.register(getClass(), NativeLibrary.getInstance("testlib", options)); } } public void testGetTypeMapperForDirectMapping() { final TypeMapper mapper = new DefaultTypeMapper(); Map options = new HashMap(); options.put(Library.OPTION_TYPE_MAPPER, mapper); DirectMapping lib = new DirectMapping(options); assertEquals("Wrong type mapper for direct mapping", mapper, Native.getTypeMapper(DirectMapping.class)); assertEquals("Wrong type mapper for direct mapping nested structure", mapper, Native.getTypeMapper(DirectMapping.DirectStructure.class)); assertEquals("Wrong type mapper for direct mapping nested callback", mapper, Native.getTypeMapper(DirectMapping.DirectCallback.class)); } private static class TestCallback implements Callback { public static final TypeMapper TYPE_MAPPER = new DefaultTypeMapper(); public void callback() { } } public void testGetTypeMapperFromCallbackInterface() throws Exception { assertEquals("Wrong type mapper for callback class", TestCallback.TYPE_MAPPER, Native.getTypeMapper(TestCallback.class)); } public static void main(String[] args) { junit.textui.TestRunner.run(NativeTest.class); } }