sistema - Prueba de unidad clase Java que carga biblioteca nativa
sistema de biblioteca en java netbeans (6)
Hay una forma de configurar la ruta de la biblioteca de la máquina virtual ejecutada por Gradle para las pruebas de unidad local, y voy a describirla a continuación, pero spoiler: en mi experiencia, @ThanosFisherman tiene razón: la unidad local prueba cosas que usan el NDK de Android parece ser un mandado de tontos en este momento.
Entonces, para cualquier otra persona que busque una manera de cargar bibliotecas compartidas (es decir, .so
) en pruebas unitarias con gradle, aquí está el resumen algo largo:
El objetivo es establecer la ruta de búsqueda de la biblioteca compartida para la JVM que ejecuta las pruebas unitarias.
Aunque muchas personas sugieren poner la ruta lib en java.library.path
, encontré que no funciona , al menos no en mi máquina Linux. (también, los mismos resultados en este hilo CodeRanch )
Lo que sí funciona es configurar la LD_LIBRARY_PATH
entorno LD_LIBRARY_PATH
os (o PATH
es el sinónimo más cercano en Windows)
Usando Gradle:
// module-level build.gradle
apply plugin: ''com.android.library'' // or application
android {
...
testOptions {
unitTests {
all {
// This is where we have access to the properties of gradle''s Test class,
// look it up if you want to customize more test parameters
// next we take our cmake output dir for whatever architecture
// you can also put some 3rd party libs here, or override
// the implicitly linked stuff (libc, libm and others)
def libpath = '''' + projectDir + ''/build/intermediates/cmake/debug/obj/x86_64/''
+'':/home/developer/my-project/some-sdk/lib''
environment ''LD_LIBRARY_PATH'', libpath
}
}
}
}
Con eso, puede ejecutar, por ejemplo ./gradlew :mymodule:testDebugUnitTest
y las ./gradlew :mymodule:testDebugUnitTest
nativas se buscarán en las rutas que especificó.
Uso del complemento JUnit de Android Studio Para el complemento JUnit de Android Studio, puede especificar las opciones de VM y las variables de entorno en la configuración de la prueba, así que simplemente ejecute una prueba JUnit (haciendo clic derecho en un método de prueba o lo que sea) y luego edite Run Configuración:
Aunque suena como "misión cumplida", descubrí que cuando uso libc.so, libm.so
y otros de mi os /usr/lib
me da errores de versión (probablemente porque mi propia biblioteca está compilada por cmake con android ndk toolkit es su propia plataforma libs). Y el uso de las librerías de plataforma de los paquetes ndk redujo la JVM con un error SIGSEGV
(debido a la incompatibilidad de las libs de la plataforma ndk con el entorno host os)
Actualización Como @AlexCohn señaló incisivamente en los comentarios, uno tiene que compilar contra las librerías del entorno de host para que esto funcione; aunque su máquina probablemente sea x86_64 , los binarios x86_64 construidos en entorno NDK no funcionarán.
Puede haber algo que pase por alto, obviamente, y agradeceré cualquier comentario, pero por ahora estoy abandonando la idea a favor de las pruebas instrumentadas.
Estoy ejecutando pruebas unitarias en Android Studio. Tengo una clase de Java que carga una biblioteca nativa con el siguiente código
static
{
System.loadLibrary("mylibrary");
}
Pero cuando pruebo esta clase dentro de mi directorio src/test
obtengo
java.lang.UnsatisfiedLinkError: no mylibrary in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1864)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
¿Cómo puedo hacer que encuentre la ruta de las librerías nativas .so que se encuentra en src/main/libs
para poder realizar pruebas unitarias sin errores?
Nota: dentro del directorio src/main/libs
tengo 3 subdirectorios más: armeabi
, mips
y x86
. Cada uno de ellos contiene el archivo .so adecuado. Estoy usando la versión no experimental para construir librerías NDK.
No quiero usar otras bibliotecas de prueba de terceros, ya que todas mis otras clases de Java "puras" pueden ser probadas en una sola unidad. Pero si eso no es posible, entonces estoy abierto a otras alternativas.
Aquí está mi código de prueba que arroja el error
@Test
public void testNativeClass() throws Exception
{
MyNativeJavaClass test = new MyNativeJavaClass("lalalal")
List<String> results = test.getResultsFromNativeMethodAndPutThemInArrayList();
assertEquals("There should be only three result", 3, results.size());
}
Intente ejecutar el código de prueba con la opción java -XshowSettings: properties y asegúrese de que su ruta de destino para las bibliotecas del sistema y en el resultado de este comando, los valores de ruta de la biblioteca sean los mismos
La única solución que encontré que funciona sin hacks es usar JUnit a través de pruebas de instrumentación (directorio androidTest). Mi clase ahora se puede probar bien, pero con la ayuda del dispositivo Android o el emulador.
Los archivos .so se colocarán debajo
src / main / jniLibs
No bajo src / main / libs
(Probado con Android Studio 1.2.2)
Como referencia, consulte la página - http://ph0b.com/android-studio-gradle-and-ndk-integration/ , aunque algunas partes pueden estar desactualizadas.
No estoy seguro de si esto resuelve su problema o no, pero hasta ahora nadie ha mencionado el patrón de estrategia para tratar con las clases que precargan la biblioteca durante su creación.
Veamos el ejemplo:
Queremos implementar la clase de solucionador de Fibonacci. Suponiendo que proporcionamos la implementación en el código nativo y logramos generar la biblioteca nativa, podemos implementar lo siguiente:
public interface Fibonacci {
long calculate(int steps);
}
En primer lugar, ofrecemos nuestra implementación nativa:
public final class FibonacciNative implements Fibonacci {
static {
System.loadLibrary("myfibonacci");
}
public native long calculate(int steps);
}
En segundo lugar, proporcionamos la implementación de Java para el solucionador de Fibonacci:
public final class FibonacciJava implements Fibonacci {
@Override
public long calculate(int steps) {
if(steps > 1) {
return calculate(steps-2) + calculate(steps-1);
}
return steps;
}
}
En tercer lugar, ajustamos los solucionadores con la clase parental eligiendo su propia implementación durante la creación de instancias:
public class FibonnaciSolver implements Fibonacci {
private static final Fibonacci STRATEGY;
static {
Fibonacci implementation;
try {
implementation = new FibonnaciNative();
} catch(Throwable e) {
implementation = new FibonnaciJava();
}
STRATEGY = implementation;
}
@Override
public long calculate(int steps) {
return STRATEGY.calculate(steps);
}
}
Por lo tanto, el problema de encontrar el camino a la biblioteca usando la estrategia. Este caso, sin embargo, no resuelve el problema si la biblioteca nativa es realmente necesaria para ser incluida durante la prueba. Tampoco resuelve el problema si la biblioteca nativa es una biblioteca de terceros.
Básicamente, esto evita el problema de carga de la biblioteca nativa burlándose del código nativo para el código de Java.
Espero que esto ayude de alguna manera :)
Solo asegúrese de que el directorio que contiene la biblioteca se encuentre en la propiedad del sistema java.library.path
.
De la prueba, puede configurarlo antes de cargar la biblioteca:
System.setProperty("java.library.path", "... path to the library .../libs/x86");
Puede especificar la ruta codificada, pero esto hará que el proyecto sea menos portátil para otros entornos. Entonces sugiero que lo desarrolles programáticamente.