android en - Prueba de unidad clase Java que carga biblioteca nativa




codigo pdf (6)

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

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());
    }

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 :)



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.


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.


Use setLooping(true) en su instancia de MediaPlayer.

--Editar--

¿Qué le setOnPrepareListener usar setOnPrepareListener lugar de setOnCompletionListener? Esto le da acceso al objeto MediaPlayer.

vv.setOnPreparedListener (new OnPreparedListener() {                    
    @Override
    public void onPrepared(MediaPlayer mp) {
        mp.setLooping(true);
    }
});






java android unit-testing junit4