J'exécute des tests unitaires sous Android Studio. J'ai une classe Java qui charge une bibliothèque native avec le code suivant
static
{
System.loadLibrary("mylibrary");
}
Mais lorsque je teste cette classe dans mon répertoire src/test
, je reçois
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 le faire trouver le chemin des bibliothèques .so natives qui se trouve à src/main/libs
afin de tester unités sans erreurs?
Remarque: dans le répertoire src/main/libs
, j'ai 3 autres sous-répertoires: armeabi
, mips
et x86
. Chacun de ceux-ci contient le fichier .so approprié. J'utilise la version Non expérimentale pour construire des 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 correctement. Mais si ce n'est pas possible, je suis ouvert aux alternatives.
Voici mon code de test qui jette 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());
}
La seule solution que j'ai trouvée qui fonctionne sans piratage consiste à utiliser JUnit via des tests d'instrumentation (répertoire androidTest) . Ma classe peut désormais être testée correctement, mais à l'aide du périphérique ou de l'émulateur Android.
Je ne sais pas si cela résoudra votre problème ou non, mais personne n’a encore mentionné le modèle de stratégie pour traiter les bibliothèques de préchargement de classes lors de leur création.
Voyons l'exemple:
Nous voulons implémenter la classe de solveur de Fibonacci. En supposant que nous ayons fourni l'implémentation dans le code natif et que nous ayons réussi à générer la bibliothèque native, nous pouvons implémenter les éléments suivants:
public interface Fibonacci {
long calculate(int steps);
}
Tout d'abord, nous fournissons notre implémentation native:
public final class FibonacciNative implements Fibonacci {
static {
System.loadLibrary("myfibonacci");
}
public native long calculate(int steps);
}
Deuxièmement, nous fournissons une implémentation Java pour le ré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 trouver le 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 doit vraiment être incluse pendant le test. Cela ne résout pas non plus le problème si la bibliothèque native est une bibliothèque tierce.
Fondamentalement, cela résout le problème de chargement de la bibliothèque native en récupérant le code natif pour le code Java.
J'espère que cela aide en quelque sorte :)
Il est un moyen de configurer le chemin de bibliothèque de Gradle-run VM pour les tests d'unité locale, et je vais le décrire ci-dessous, mais spoiler: dans mon expérience, @ThanosFisherman a raison: les tests unitaires locaux pour les éléments qui utilisent le NDK Android semblent être une folle course en ce moment.
Donc, pour tous ceux qui cherchent un moyen de charger des bibliothèques partagées (c'est-à-dire .so
) dans des tests unitaires avec gradle, voici l'abstrait un peu long:
L'objectif est de définir le chemin de recherche de la bibliothèque partagée pour la machine virtuelle Java exécutant les tests unitaires.
Bien que beaucoup de gens suggèrent de mettre le chemin de la bibliothèque dans Java.library.path
, j’ai trouvé que cela ne fonctionne pas, du moins pas sur ma machine Linux. (également, mêmes résultats dans ce fil CodeRanch )
Qu'est-ce que fonctionne bien que définisse la variable d'environnement LD_LIBRARY_PATH
os (ou PATH
est le synonyme le plus proche sous Windows)
En utilisant 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 seront recherchées dans les chemins que vous avez spécifiés.
Utilisation du plug-in Android Studio JUnit Pour le plug-in JUnit d'Android Studio, vous pouvez spécifier les options VM et les variables d'environnement dans les paramètres de la configuration du test. en cliquant sur une méthode de test ou quoi que ce soit) puis éditez la configuration d'exécution:
Bien que cela ressemble à "mission accomplie", j'ai constaté qu'en utilisant libc.so, libm.so
et d'autres de mon système d'exploitation, /usr/lib
me donnait des erreurs de version (probablement parce que ma propre bibliothèque est compilée par cmake avec le toolkit ndk Android contre ses propres librairies de plate-forme). Et l'utilisation des bibliothèques de plate-forme à partir des packages ndk a entraîné la machine virtuelle Java avec une erreur SIGSEGV
(en raison de l'incompatibilité des bibliothèques de la plate-forme ndk avec l'environnement Host OS).
Update Comme @AlexCohn l'a souligné de manière incisive dans les commentaires, il faut construire avec les bibliothèques de l'environnement hôte pour que cela fonctionne; Même si votre machine est probablement x86_64, les fichiers binaires x86_64 créés dans l'environnement NDK ne fonctionneront pas.
Il y a peut-être quelque chose que j'ai négligé, évidemment, et j'apprécierai tout retour d'information, mais pour l'instant, je laisse tomber toute cette idée en faveur des tests instrumentés.
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 vers d'autres environnements. Donc, je vous suggère de le construire par programme.
Essayez d’exécuter votre code de test avec l’option Java -XshowSettings: properties et assurez-vous que le chemin de destination des bibliothèques système et dans le résultat de cette commande, les valeurs du chemin de la bibliothèque sont les mêmes.
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, consultez la page - http://ph0b.com/Android-studio-gradle-and-ndk-integration/ , bien que certaines parties puissent être obsolètes.