Unit test classe Java che carica libreria nativa


Sto eseguendo test unitari in Android Studio. Ho una classe Java che carica una libreria nativa con il seguente codice

 static
    {
       System.loadLibrary("mylibrary");
    }

Ma quando provo questa classe nella mia directory src/test ottengo

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)

Come posso farlo trovare il percorso delle librerie native .so che si trova in src/main/libs per testare l'unità senza errori?

Nota: all'interno della directory src/main/libs ho altre 3 sottodirectory: armeabi, mips e x86. Ognuno di questi contiene il file .so corretto. Sto usando il Versione non sperimentale per la creazione di librerie NDK.

Non voglio usare altre librerie di test di terze parti poiché tutte le mie altre classi java "pure" possono essere testate correttamente. Ma se questo non è possibile, allora sono aperto alle alternative.

Ecco il mio codice di test che genera l'errore

   @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());
    }
Author: JulianHarty, 2016-01-15

6 answers

L'unica soluzione che ho trovato che funziona senza hack è usare JUnit attraverso il test di strumentazione (directory androidTest). La mia classe può ora essere testata bene ma con l'aiuto del dispositivo Android o dell'emulatore.

 9
Author: ThanosFisherman, 2016-02-03 23:51:01

Non sono sicuro che questo risolva il tuo problema o meno, ma finora nessuno ha menzionato il modello di strategia per gestire le classi che precaricano la libreria durante la loro creazione.

Vediamo l'esempio:

Vogliamo implementare la classe risolutore di Fibonacci. Supponendo che abbiamo fornito l'implementazione nel codice nativo e siamo riusciti a generare la libreria nativa, possiamo implementare quanto segue:

public interface Fibonacci {
     long calculate(int steps);
}

In primo luogo, forniamo il nostro nativo attuazione:

public final class FibonacciNative implements Fibonacci {
    static {
      System.loadLibrary("myfibonacci");
    }

    public native long calculate(int steps);
}

In secondo luogo, forniamo l'implementazione Java per il risolutore di 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;
   }
}

In terzo luogo, avvolgiamo i risolutori con la classe parentale che sceglie la propria implementazione durante la sua istanziazione:

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

}

Così, il problema con la ricerca di percorso per la libreria utilizzando la strategia. Questo caso, tuttavia, non risolve il problema se la libreria nativa è davvero necessaria per essere inclusa durante il test. Non risolve il problema se la libreria nativa è un libreria di terze parti.

Fondamentalmente, questo risolve il problema di caricamento della libreria nativa prendendo in giro il codice nativo per il codice java.

Spero che questo aiuti in qualche modo:)

 3
Author: dawid gdanski, 2017-04-14 05:59:44

c'è un modo per configurare il percorso della libreria della VM di Gradle-run per i test unitari locali, e lo descriverò di seguito, ma spoiler: nella mia esperienza, @ThanosFisherman ha ragione: i test unitari locali per cose che usano Android NDK sembrano essere una commissione sciocca in questo momento.

Quindi, per chiunque altro cerchi un modo per caricare librerie condivise (cioè .so) in unit test con gradle, ecco l'abstract un po ' lungo:

L'obiettivo è impostare la ricerca della libreria condivisa percorso per la JVM che esegue i test unitari.

Anche se molte persone suggeriscono di inserire il percorso lib in java.library.path, ho scoperto chenon funziona , almeno non sulla mia macchina linux. (inoltre, stessi risultati in questo thread CodeRanch)

Ciò che funziona sta impostando la variabile d'ambiente LD_LIBRARY_PATH os (o PATH è il sinonimo più vicino in Windows)

Utilizzo di 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 questo, puoi eseguire, ad esempio ./gradlew :mymodule:testDebugUnitTest e le librerie native sarà cercato nei percorsi che hai specificato.

Utilizzo del plugin JUnit di Android Studio Per il plug-in JUnit di Android Studio, è possibile specificare le opzioni VM e le variabili di ambiente nelle impostazioni della configurazione di test, quindi basta eseguire un test JUnit (facendo clic con il pulsante destro del mouse su un metodo di test o qualsiasi altra cosa) e quindi modificare la configurazione di esecuzione: inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Anche se suona come "missione compiuta" , ho scoperto che quando si utilizza libc.so, libm.so e altri dal mio sistema operativo /usr/lib mi dà errori di versione (probabilmente perché la mia libreria è compilata da cmake con il toolkit ndk Android contro le sue librerie di piattaforma). E l'uso delle librerie di piattaforma dai pacchetti ndk ha fatto cadere la JVM con un errore SIGSEGV (a causa dell'incompatibilità delle librerie della piattaforma ndk con l'ambiente os host)

Update Come @ AlexCohn ha sottolineato in modo incisivo nei commenti, bisogna costruire contro le librerie dell'ambiente host perché funzioni; anche se il tuo la macchina molto probabilmente è x86_64, i binari x86_64 costruiti contro l'ambiente NDK non lo faranno.

Potrebbe esserci qualcosa che ho trascurato, ovviamente, e apprezzerò qualsiasi feedback, ma per ora sto abbandonando l'idea a favore di test strumentati.

 1
Author: Ivan Bartsov, 2017-11-03 14:49:38

Assicurati che la directory contenente la libreria sia contenuta nella proprietà di sistema java.library.path.

Dal test è possibile impostarlo prima di caricare la libreria:

System.setProperty("java.library.path", "... path to the library .../libs/x86");

È possibile specificare il percorso hard coded, ma questo renderà il progetto meno portabile ad altri ambienti. Quindi ti suggerisco di costruirlo a livello di programmazione.

 0
Author: Henry, 2016-01-26 15:17:50

I file. so devono essere posizionati sotto

Src / main / jniLibs

Non in src / main / libs

(Testato con Android Studio 1.2.2)

Per riferimento controllare la pagina - http://ph0b.com/android-studio-gradle-and-ndk-integration / , anche se alcune porzioni potrebbero essere obsolete.

 0
Author: prabindh, 2016-01-31 18:21:26

Prova a eseguire il codice di prova con l'opzione java-XshowSettings: properties e assicurati che il percorso di destinazione per le librerie di sistema e nell'output di questo comando, i valori del percorso della libreria siano gli stessi

 0
Author: daemonThread, 2016-02-02 06:25:11