Подтвердить что ты не робот

Unit test Java-класс, который загружает собственную библиотеку

Я запускаю модульные тесты в Android Studio. У меня есть класс Java, который загружает собственную библиотеку со следующим кодом

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

Но когда я тестирую этот класс внутри моего каталога src/test, я получаю

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)

Как мне найти путь к родным .so-библиотекам, который находится в src/main/libs для unit test без ошибок?

Примечание: внутри каталога src/main/libs У меня есть еще 3 подкаталога: armeabi, mips и x86. Каждый из них содержит правильный .so файл. Я использую Не экспериментальную версию для создания библиотек NDK.

Я не хочу использовать другие сторонние библиотеки тестирования, так как все мои другие "чистые" классы Java могут быть проверены в порядке. Но если это невозможно, то я открыт для альтернатив.

Вот мой тестовый код, который выдает ошибку

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

Ответ 1

Единственное решение, которое я нашел, что работает без хаков, - это использовать JUnit с помощью инструментального тестирования (каталог androidTest). Мой класс теперь может быть протестирован отлично, но с помощью устройства или эмулятора Android.

Ответ 2

Я не уверен, решает ли это вашу проблему или нет, но пока никто не упомянул о шаблоне стратегии для работы с библиотекой предзагрузок классов во время их создания.

Посмотрим на пример:

Мы хотим реализовать класс решателя Fibonacci. Предполагая, что мы обеспечили реализацию в собственном коде и сумели создать собственную библиотеку, мы можем реализовать следующее:

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

Во-первых, мы предоставляем нашу собственную реализацию:

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

    public native long calculate(int steps);
}

Во-вторых, мы предоставляем реализацию Java для решения 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;
   }
}

В-третьих, мы завершаем решателей родительским классом, выбирая его собственную реализацию во время его создания:

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

}

Таким образом, проблема с поиском пути к библиотеке с использованием стратегии. Однако этот случай не решает проблему, если необходимо, чтобы встроенная библиотека была включена во время теста. Он не решает проблему, если родная библиотека является сторонней библиотекой.

В основном, это обходит проблему загрузки исходной библиотеки, выбирая собственный код для Java-кода.

Надеюсь, это поможет как-то:)

Ответ 3

Просто убедитесь, что каталог, содержащий библиотеку, содержится в системном свойстве java.library.path.

Из теста вы можете установить его перед загрузкой библиотеки:

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

Вы можете указать путь жестко закодированным, но это сделает проект менее переносимым для других сред. Поэтому я предлагаю вам создать его программно.

Ответ 4

Там есть способ настройки пути библиотеки Gradle -run VM для локальных тестов, и я расскажу об этом ниже, но спойлер: в моем эксперименте @ThanosFisherman право: тесты локальных блоков для вещей, которые используют Android NDK, кажутся безумными. Теперь

Итак, для всех, кто ищет способ загрузки общих (например, .so) библиотек в модульные тесты с помощью gradle, здесь несколько длинный реферат:

Цель состоит в том, чтобы установить путь поиска общей библиотеки для JVM, выполняющей модульные тесты.

Althoug Многие люди предлагают положить путь lib в java.library.path, я обнаружил, что он не работает, по крайней мере, не на моей Linux-машине. (также те же результаты в этом потоке CodeRanch)

Что работает, но устанавливает переменную среды LD_LIBRARY_PATH os (или PATH является ближайшим синонимом в Windows)

Использование 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 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
            }
        }
    }
}

С этим вы можете запускать, например. ./gradlew :mymodule:testDebugUnitTest, и вложенные библиотеки будут искать в указанных вами путях.

Использование плагина Android Studio JUnit Для плагина Android Studio JUnit вы можете указать параметры виртуальной машины и переменные среды в настройках тестовой конфигурации, поэтому просто запустите JUnit-тест (щелкните правой кнопкой мыши на методе тестирования или что-то еще), а затем отредактируйте Run Configuration: введите описание изображения здесь введите описание изображения здесь

Хотя это звучит как "выполняемая миссия", я обнаружил, что при использовании libc.so, libm.so и других из моего os /usr/lib мне дают ошибки в версии (возможно, потому что моя собственная библиотека скомпилирована cmake с помощью инструментария android ndk против него платформы libs). И использование платформы libs из пакетов ndk привело к ошибке JVM с ошибкой SIGSEGV (из-за несовместимости библиотек платформы ndk с окружением хоста os)

Обновить. Как @AlexCohn резко указал в комментариях, нужно построить против lib среды для хоста, чтобы это работало; даже если ваша машина, скорее всего, x86_64, двоичные файлы x86_64, созданные против среды NDK, не будут выполняться.

Возможно, я кое-что упустил из виду, и я буду благодарен за любую обратную связь, но на данный момент я отбросил всю идею в пользу инструментальных тестов.

Ответ 5

Файлы .so должны быть помещены под

SRC/главная/jniLibs

Не под src/main/libs

(Протестировано с помощью Android Studio 1.2.2)

Для справки проверьте страницу http://ph0b.com/android-studio-gradle-and-ndk-integration/, хотя некоторые части могут быть устаревшими.

Ответ 6

Попробуйте запустить тестовый код с параметром java -XshowSettings: properties и убедитесь, что ваш путь назначения для системных библиотек и в результате вывода этой команды значения пути к библиотеке совпадают.

Ответ 7

Если для теста требуется библиотека, используйте тест AndroidTest (в разделе src/androidTest/...), а не тест джунита. Это позволит вам загружать и использовать нативную библиотеку, как и в других местах вашего кода.

Если библиотека не требуется для вашего теста, просто оберните загрузку системы в try/catch. Это позволит классу JNI по-прежнему работать в тестах junit (в src/test/...), и это безопасный обходной путь, учитывая, что он вряд ли замаскирует ошибку (что-то еще, безусловно, завершится ошибкой, если родная библиотека на самом деле нужно). Оттуда вы можете использовать что-то вроде mockito, чтобы заглушить любые вызовы методов, которые все еще попадают в библиотеку JNI.

Например в Котлине:

    companion object {
        init {
            try {
                System.loadLibrary("mylibrary")
            } catch (e: UnsatisfiedLinkError) {
                // log the error or track it in analytics
            }
        }
    }