Test porównawczy w ciągłej integracji

W trybie ciągłej integracji (CI) możesz przeprowadzać testy porównawcze, aby śledzić wydajność na przestrzeni czasu i wykrywać spadki lub ulepszenia, jeszcze zanim aplikacja zostanie wydana. Ta strona zawiera podstawowe informacje o testach porównawczych w CI.

Zanim zaczniesz korzystać z analizy porównawczej w CI, zastanów się, czym różni się zbieranie i ocenianie wyników od zwykłych testów.

Wyniki przybliżone

Chociaż testy porównawcze są testami instrumentalnymi, ich wyniki to nie wszystko. Porównania pozwalają na pomiar czasu w przypadku danego urządzenia, na którym działają. Wykresy wyników w czasie umożliwiają monitorowanie zmian i szumów w systemie pomiarów.

Korzystaj z prawdziwych urządzeń

przeprowadzanie testów porównawczych na fizycznych urządzeniach z Androidem; Można ich używać w emulatorach, ale zdecydowanie odradzamy ich stosowanie, ponieważ nie pokazują realistycznego doświadczenia użytkownika. Zamiast tego zawierają liczby powiązane z możliwościami systemu operacyjnego hosta i sprzętu. Rozważ wykorzystanie prawdziwych urządzeń lub usługi, która umożliwia przeprowadzanie testów na rzeczywistych urządzeniach, np. Laboratorium Firebase.

Przeprowadź testy porównawcze

Uruchamianie testów porównawczych w ramach potoku CI może się różnić od uruchamiania go lokalnie z Androida Studio. Lokalnie przeprowadza się zwykle testy integracji z Androidem z 1 zadaniem Gradle connectedCheck. To zadanie automatycznie tworzy plik APK i testowy plik APK oraz przeprowadza testy na urządzeniach połączonych z serwerem CI. Jeśli działa on w CI, zwykle trzeba podzielić go na osobne fazy.

Budowanie

W przypadku biblioteki Microbenchmark uruchom zadanie Gradle assemble[VariantName]AndroidTest, które utworzy testowy plik APK zawierający zarówno kod Twojej aplikacji, jak i testowany kod.

Biblioteka Macrobenchmark wymaga też utworzenia docelowego pliku APK i samodzielnego przetestowania go. W związku z tym uruchamiaj zadania :app:assemble[VariantName] i :macrobenchmark:assemble[VariantName] Gradle.

Zainstaluj i uruchom

Te kroki zwykle wykonuje się bez konieczności uruchamiania zadań Gradle. Pamiętaj, że mogą one być wyodrębnione w zależności od tego, czy używasz usługi, która umożliwia przeprowadzanie testów na rzeczywistych urządzeniach.

Aby przeprowadzić instalację, użyj polecenia adb install i podaj testowy plik APK lub docelowy plik APK.

Uruchom polecenie instrumentu adb shell am, aby wykonać wszystkie testy porównawcze:

adb shell am instrument -w com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

Gdy korzystasz z biblioteki Macroporównania, jako uruchamiających instrumentów używaj zwykłego narzędzia androidx.test.runner.AndroidJUnitRunner.

Za pomocą argumentu -e możesz przekazywać te same argumenty instrumentacji co w konfiguracji Gradle. Wszystkie opcje argumentów instrumentacji znajdziesz w sekcjach Argumenty instrumentacji mikroporównawczej i Dodawanie argumentów narzędziowych do analizy porównawczej makr.

Możesz na przykład ustawić argument dryRunMode, aby uruchamiać mikroporównania w ramach procesu weryfikacji żądania pull. Gdy ta flaga jest włączona, mikroporównania działają tylko w pojedynczej pętli i sprawdzają, czy działają prawidłowo, ale ich wykonanie nie trwa zbyt długo.

adb shell am instrument -w -e "androidx.benchmark.dryRunMode.enable" "true" com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

Więcej informacji o przeprowadzaniu testów instrumentów z poziomu wiersza poleceń znajdziesz w artykule Przeprowadzanie testów przy użyciu ADB.

Zablokuj zegary

Wtyczka Microbenchmark Gradle udostępnia polecenie ./gradlew lockClocks blokujące zegary procesora w przypadku urządzenia z dostępem do roota. Jest to przydatne dla zapewnienia stabilności, gdy masz dostęp do urządzeń z dostępem do roota, na przykład kompilacji „userdebug”. Możesz to powielić za pomocą skryptu powłoki lockClocks.sh, który jest dostępny w źródle biblioteki.

Możesz uruchomić skrypt bezpośrednio z hosta z systemem Linux lub Mac albo przekazać go na urządzenie za pomocą kilku poleceń adb:

adb push path/lockClocks.sh /data/local/tmp/lockClocks.sh
adb shell /data/local/tmp/lockClocks.sh
adb shell rm /data/local/tmp/lockClocks.sh

Jeśli uruchomisz skrypt powłoki bezpośrednio na hoście, wyśle on te polecenia do połączonego urządzenia.

Więcej informacji o tym, dlaczego warto blokować zegary procesora, znajdziesz w artykule o uzyskaniu spójnych testów porównawczych.

Zebranie wyników

Biblioteki testów porównawczych podają wyniki pomiarów w JSON wraz z logami profilowania do katalogu na urządzeniu z Androidem po każdym przeprowadzeniu analizy porównawczej. Biblioteka Macrobenchmark generuje wiele plików śledzenia, po jednym na zmierzoną iterację każdej pętli MacrobenchmarkRule.measureRepeated. Natomiast mikroporównanie tworzy tylko 1 plik śledzenia dla wszystkich iteracji każdego BenchmarkRule.measureRepeated. W tym samym katalogu są również dane wyjściowe profilowania.

Zapisz i zlokalizuj pliki

Jeśli przeprowadzisz testy porównawcze za pomocą Gradle, pliki te zostaną automatycznie skopiowane do katalogu danych wyjściowych komputera hosta w folderze build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/.

Jeśli uruchamiasz aplikację bezpośrednio za pomocą polecenia adb, pliki musisz pobrać ręcznie. Domyślnie raporty są zapisywane na urządzeniu, w katalogu multimediów w zewnętrznej pamięci masowej testowanej aplikacji. Dla wygody biblioteka wydrukuje ścieżkę pliku w Logcat. Pamiętaj, że folder wyjściowy może się różnić w zależności od wersji Androida, na której działają testy porównawcze.

Benchmark: writing results to /storage/emulated/0/Android/media/com.example.macrobenchmark/com.example.macrobenchmark-benchmarkData.json

Lokalizację, w której na urządzeniu są zapisywane raporty analizy porównawczej, możesz też skonfigurować, używając argumentu instrumentacji additionalTestOutputDir. Aplikacja musi mieć możliwość zapisu w tym folderze.

adb shell am instrument -w -e additionalTestOutputDir /sdcard/Download/ com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

Na Androidzie 10 (poziom interfejsu API 29) i nowszych testy aplikacji są domyślnie przeprowadzane w piaskownicy pamięci masowej, co uniemożliwia aplikacji dostęp do plików spoza katalogu związanego z aplikacją. Aby móc zapisać pliki w katalogu globalnym, takim jak /sdcard/Download, przekaż ten argument instrumentacji:

-e no-isolated-storage true

Musisz też wyraźnie zezwolić na używanie starszych opcji miejsca na dane w pliku manifestu testu porównawczego:

<application android:requestLegacyExternalStorage="true" ... >

Więcej informacji znajdziesz w artykule o tymczasowym wyłączaniu miejsca na dane o zakresie.

Pobierz pliki

Aby pobrać wygenerowane pliki z urządzenia, użyj polecenia adb pull, które pobierze określony plik do bieżącego katalogu na hoście:

adb pull /storage/emulated/0/Android/media/com.example.macrobenchmark/com.example.macrobenchmark-benchmarkData.json

Aby pobrać wszystkie pliki benchmarkData z określonego folderu, sprawdź ten fragment kodu:

# The following command pulls all files ending in -benchmarkData.json from the directory
# hierarchy starting at the root /storage/emulated/0/Android.
adb shell find /sdcard/Download -name "*-benchmarkData.json" | tr -d '\r' | xargs -n1 adb pull

Pliki śledzenia (.trace lub .perfetto-trace) są zapisywane w tym samym folderze co benchmarkData.json, możesz więc zbierać je w ten sam sposób.

Przykład danych analizy porównawczej

Biblioteki porównawcze generują pliki JSON zawierające informacje o urządzeniu, na którym uruchomiono testy porównawcze, oraz rzeczywiste testy porównawcze. Ten fragment kodu reprezentuje wygenerowany plik JSON:

{
    "context": {
        "build": {
            "brand": "google",
            "device": "blueline",
            "fingerprint": "google/blueline/blueline:12/SP1A.210812.015/7679548:user/release-keys",
            "model": "Pixel 3",
            "version": {
                "sdk": 31
            }
        },
        "cpuCoreCount": 8,
        "cpuLocked": false,
        "cpuMaxFreqHz": 2803200000,
        "memTotalBytes": 3753299968,
        "sustainedPerformanceModeEnabled": false
    },
    "benchmarks": [
        {
            "name": "startup",
            "params": {},
            "className": "com.example.macrobenchmark.startup.SampleStartupBenchmark",
            "totalRunTimeNs": 4975598256,
            "metrics": {
                "timeToInitialDisplayMs": {
                    "minimum": 347.881076,
                    "maximum": 347.881076,
                    "median": 347.881076,
                    "runs": [
                        347.881076
                    ]
                }
            },
            "sampledMetrics": {},
            "warmupIterations": 0,
            "repeatIterations": 3,
            "thermalThrottleSleepSeconds": 0
        }
    ]
}

Dodatkowe materiały