Classe Java de test unitaire qui charge la bibliothèque native


J'exécute des tests unitaires dans Android Studio. J'ai une classe Java qui charge une bibliothèque native avec le code suivant

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

Mais quand je teste cette classe dans mon répertoire src/test j'obtiens

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)

Comment puis-je lui faire trouver le chemin des bibliothèques natives .so qui est situé à src/main/libs afin de tester l'unité sans erreurs?

Remarque: à l'intérieur du répertoire src/main/libs j'ai 3 sous-répertoires supplémentaires: armeabi, mips et x86. Chacun d'entre eux contient le fichier .so approprié. Je suis à l'aide de la Version non expérimentale pour la construction de bibliothèques NDK.

Je ne veux pas utiliser d'autres bibliothèques de test tierces car toutes mes autres classes java "pures" peuvent être testées à l'unité. Mais si ce n'est pas possible, je suis ouvert à des alternatives.

Voici mon code de test qui renvoie l'erreur

   @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

La seule solution que j'ai trouvée qui fonctionne sans hacks est d'utiliser JUnit via les tests d'instrumentation (répertoire androidTest). Ma classe peut maintenant être testée correctement mais avec l'aide de l'appareil Android ou de l'émulateur.

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

Je ne sais pas si cela résout votre problème ou non, mais jusqu'à présent, personne n'a mentionné le modèle de stratégie pour traiter la bibliothèque de préchargement des classes lors de leur création.

Voyons l'exemple:

Nous voulons implémenter la classe de solveur Fibonacci. En supposant que nous avons fourni une implémentation dans le code natif et que nous avons réussi à générer la bibliothèque native, nous pouvons implémenter ce qui suit:

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

Tout d'abord, nous fournissons notre natif mise en œuvre:

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

    public native long calculate(int steps);
}

Deuxièmement, nous fournissons l'implémentation Java pour le solveur 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;
   }
}

Troisièmement, nous enveloppons les solveurs avec la classe parentale choisissant sa propre implémentation lors de son instanciation:

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

}

Ainsi, le problème avec la recherche de chemin d'accès à la bibliothèque en utilisant la stratégie. Ce cas, cependant, ne résout pas le problème si la bibliothèque native est vraiment nécessaire pour être incluse pendant le test. Il ne résout pas non plus le problème si la bibliothèque native est un la troisième partie de la bibliothèque.

Fondamentalement, cela permet de contourner le problème de chargement de la bibliothèque native en se moquant du code natif pour le code java.

J'espère que cela aide en quelque sorte:)

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

Il y a un moyen de configurer le chemin de la bibliothèque de la VM Gradle-run pour les tests unitaires locaux, et je vais le décrire ci-dessous, mais spoiler: dans mon expérience, @ThanosFisherman a raison: les tests unitaires locaux pour les choses qui utilisent le NDK Android semblent être une erreur idiote en ce moment.

Donc, pour quiconque cherche un moyen de charger des bibliothèques partagées (c'est-à-dire .so) dans des tests unitaires avec gradle, voici le résumé un peu long:

L'objectif est de définir la recherche de bibliothèque partagée chemin d'accès pour la JVM exécutant les tests unitaires.

Bien que beaucoup de gens suggèrent de mettre le chemin lib dans java.library.path, j'ai trouvé que ne fonctionne pas, du moins pas sur ma machine Linux. (aussi, les mêmes résultats dans ce thread CodeRanch)

Qu'est-ce que fonctionne bien que définit la variable d'environnement LD_LIBRARY_PATH os (ou PATH est le synonyme le plus proche dans Windows)

À L'Aide De 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
            }
        }
    }
}

Avec cela, vous pouvez exécuter, par exemple ./gradlew :mymodule:testDebugUnitTest et les bibliothèques natives sera recherché dans les chemins que vous avez spécifiés.

Utilisation du plugin Android Studio JUnit Pour le plugin JUnit d'Android Studio, vous pouvez spécifier les options de la machine virtuelle et les variables d'environnement dans les paramètres de la configuration de test, il suffit donc d'exécuter un test JUnit (clic droit sur une méthode de test ou autre), puis de modifier la configuration d'exécution: entrez la description de l'image ici entrez la description de l'image ici

Bien que cela ressemble à "mission accomplie", j'ai trouvé que lors de l'utilisation de libc.so, libm.so et d'autres de mon système d'exploitation /usr/lib me donne des erreurs de version (probablement parce que ma propre bibliothèque est compilée par cmake avec la boîte à outils Android ndk contre ses propres bibliothèques de plate-forme). Et l'utilisation des bibliothèques de plate-forme des paquets ndk a fait tomber la JVM avec une erreur SIGSEGV (en raison de l'incompatibilité des bibliothèques de plate-forme ndk avec l'environnement du système d'exploitation hôte)

Update Comme @AlexCohn l'a souligné de manière incisive dans les commentaires, il faut construire contre les bibliothèques de l'environnement hôte pour que cela fonctionne; même si votre la machine est très probablement x86_64, les binaires x86_64 construits dans l'environnement NDK ne le feront pas.

Il y a peut-être quelque chose que j'ai négligé, évidemment, et j'apprécierai tout retour, mais pour l'instant j'abandonne l'idée en faveur de tests instrumentés.

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

Assurez-vous simplement que le répertoire contenant la bibliothèque est contenu dans la propriété système java.library.path.

À partir du test, vous pouvez le définir avant de charger la bibliothèque:

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

Vous pouvez spécifier le chemin codé en dur, mais cela rendra le projet moins portable dans d'autres environnements. Je vous suggère donc de le construire par programme.

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

Les fichiers. so doivent être placés sous

Src / main / jniLibs

Pas sous src/main/libs

(Testé avec Android Studio 1.2.2)

Pour référence, vérifiez la page - http://ph0b.com/android-studio-gradle-and-ndk-integration / , bien que certaines parties puissent être obsolètes.

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

Essayez d'exécuter votre code de test avec l'option java-XshowSettings:properties et assurez-vous que votre chemin de destination pour les bibliothèques système et dans la sortie de cette commande, les valeurs de chemin de bibliothèque sont les mêmes

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