Add integration tests for BackgroundDexOptService
Add three tests: 1. Under normal conditions, check that dexopt upgrades test app to $(getprop pm.dexopt.bg-dexopt). 2. Under low storage conditions and package is unused, check that dexopt downgrades test app to $(getprop pm.dexopt.inactive). 3. Under low storage conditions and package is recently used, check that dexopt upgrades test app to $(getprop pm.dexopt.bg-dexopt). Test: atest -v BackgroundDexOptServiceIntegrationTests BUG: 64807719 Change-Id: Iaa50d5120ea0255b38226bda0452e7e47f1ff5d0
This commit is contained in:
34
tests/BackgroundDexOptServiceIntegrationTests/Android.mk
Normal file
34
tests/BackgroundDexOptServiceIntegrationTests/Android.mk
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# Copyright (C) 2017 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
# We only want this apk build for tests.
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
# Include all test java files.
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
android-support-test \
|
||||
|
||||
LOCAL_PACKAGE_NAME := BackgroundDexOptServiceIntegrationTests
|
||||
LOCAL_COMPATIBILITY_SUITE := device-tests
|
||||
|
||||
LOCAL_CERTIFICATE := platform
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.frameworks.bgdexopttest">
|
||||
|
||||
|
||||
<!-- Uses API introduced in O (26) -->
|
||||
<uses-sdk
|
||||
android:minSdkVersion="1"
|
||||
android:targetSdkVersion="26" />
|
||||
|
||||
<uses-permission android:name="android.permission.DUMP" />
|
||||
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
|
||||
<uses-permission android:name="android.permission.SET_TIME" />
|
||||
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation
|
||||
android:name="android.support.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="com.android.frameworks.bgdexopttest"
|
||||
android:label="Integration test for BackgroundDexOptService" />
|
||||
</manifest>
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<configuration description="Runs BackgroundDexOptService Integration Tests">
|
||||
<!--DeviceSetup should go before TimeSetter because it stops automatic update of time-->
|
||||
<target_preparer
|
||||
class="com.android.tradefed.targetprep.DeviceSetup">
|
||||
<option name="auto-update-time" value="OFF"/>
|
||||
<option name="auto-update-timezone" value="OFF"/>
|
||||
<option name="set-property" key="pm.dexopt.downgrade_after_inactive_days" value="2"/>
|
||||
<option name="set-property" key="pm.dexopt.disable_bg_dexopt" value="true"/>
|
||||
<option name="set-property" key="pm.dexopt.inactive" value="verify"/>
|
||||
<option name="set-property" key="pm.dexopt.bg-dexopt" value="speed"/>
|
||||
<option name="restore-settings" value="true"/>
|
||||
<option name="restore-properties" value="true"/>
|
||||
</target_preparer>
|
||||
|
||||
<!--Test app needs to be installed when we change its settings below-->
|
||||
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
|
||||
<option name="test-file-name" value="BackgroundDexOptServiceIntegrationTests.apk"/>
|
||||
<option name="cleanup-apks" value="true"/>
|
||||
</target_preparer>
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.SetPackagesRecentlyUsed">
|
||||
<option name="package-recently-used-time" value="0d"/>
|
||||
<option name="package-recently-used-name" value="com.android.frameworks.bgdexopttest"/>
|
||||
</target_preparer>
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.RestartSystemServerTargetPreparer"/>
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.DeviceStorageFiller">
|
||||
<!--32GB-->
|
||||
<!--necessary because a package cannot create a file larger than 100GB-->
|
||||
<option name="free-bytes" value="34359738368"/>
|
||||
</target_preparer>
|
||||
|
||||
<option name="test-suite-tag" value="apct"/>
|
||||
<option name="test-tag" value="BackgroundDexOptServiceIntegrationTests"/>
|
||||
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
|
||||
<option name="package" value="com.android.frameworks.bgdexopttest"/>
|
||||
<option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
|
||||
</test>
|
||||
</configuration>
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.pm;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.util.Log;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link BackgroundDexOptService}.
|
||||
*
|
||||
* Tests various scenarios around BackgroundDexOptService.
|
||||
* 1. Under normal conditions, check that dexopt upgrades test app to
|
||||
* $(getprop pm.dexopt.bg-dexopt).
|
||||
* 2. Under low storage conditions and package is unused, check
|
||||
* that dexopt downgrades test app to $(getprop pm.dexopt.inactive).
|
||||
* 3. Under low storage conditions and package is recently used, check
|
||||
* that dexopt upgrades test app to $(getprop pm.dexopt.bg-dexopt).
|
||||
*
|
||||
* Each test case runs "cmd package bg-dexopt-job com.android.frameworks.bgdexopttest".
|
||||
*
|
||||
* The setup for these tests make sure this package has been configured to have been recently used
|
||||
* plus installed far enough in the past. If a test case requires that this package has not been
|
||||
* recently used, it sets the time forward more than
|
||||
* `getprop pm.dexopt.downgrade_after_inactive_days` days.
|
||||
*
|
||||
* For tests that require low storage, the phone is filled up.
|
||||
*
|
||||
* Run with "atest BackgroundDexOptServiceIntegrationTests".
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public final class BackgroundDexOptServiceIntegrationTests {
|
||||
|
||||
private static final String TAG = BackgroundDexOptServiceIntegrationTests.class.getSimpleName();
|
||||
|
||||
// Name of package to test on.
|
||||
private static final String PACKAGE_NAME = "com.android.frameworks.bgdexopttest";
|
||||
// Name of file used to fill up storage.
|
||||
private static final String BIG_FILE = "bigfile";
|
||||
private static final String BG_DEXOPT_COMPILER_FILTER = SystemProperties.get(
|
||||
"pm.dexopt.bg-dexopt");
|
||||
private static final String DOWNGRADE_COMPILER_FILTER = SystemProperties.get(
|
||||
"pm.dexopt.inactive");
|
||||
private static final long DOWNGRADE_AFTER_DAYS = SystemProperties.getLong(
|
||||
"pm.dexopt.downgrade_after_inactive_days", 0);
|
||||
// Needs to be between 1.0 and 2.0.
|
||||
private static final double LOW_STORAGE_MULTIPLIER = 1.5;
|
||||
|
||||
// The file used to fill up storage.
|
||||
private File mBigFile;
|
||||
|
||||
// Remember start time.
|
||||
@BeforeClass
|
||||
public static void setUpAll() {
|
||||
if (!SystemProperties.getBoolean("pm.dexopt.disable_bg_dexopt", false)) {
|
||||
throw new RuntimeException(
|
||||
"bg-dexopt is not disabled (set pm.dexopt.disable_bg_dexopt to true)");
|
||||
}
|
||||
if (DOWNGRADE_AFTER_DAYS < 1) {
|
||||
throw new RuntimeException(
|
||||
"pm.dexopt.downgrade_after_inactive_days must be at least 1");
|
||||
}
|
||||
if ("quicken".equals(BG_DEXOPT_COMPILER_FILTER)) {
|
||||
throw new RuntimeException("pm.dexopt.bg-dexopt should not be \"quicken\"");
|
||||
}
|
||||
if ("quicken".equals(DOWNGRADE_COMPILER_FILTER)) {
|
||||
throw new RuntimeException("pm.dexopt.inactive should not be \"quicken\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Context getContext() {
|
||||
return InstrumentationRegistry.getTargetContext();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
File dataDir = getContext().getDataDir();
|
||||
mBigFile = new File(dataDir, BIG_FILE);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (mBigFile.exists()) {
|
||||
boolean result = mBigFile.delete();
|
||||
if (!result) {
|
||||
throw new RuntimeException("Couldn't delete big file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the content of the InputStream as a String.
|
||||
private static String inputStreamToString(InputStream is) throws IOException {
|
||||
char[] buffer = new char[1024];
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try (InputStreamReader reader = new InputStreamReader(is)) {
|
||||
for (; ; ) {
|
||||
int count = reader.read(buffer, 0, buffer.length);
|
||||
if (count < 0) {
|
||||
break;
|
||||
}
|
||||
builder.append(buffer, 0, count);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
// Run the command and return the stdout.
|
||||
private static String runShellCommand(String cmd) throws IOException {
|
||||
Log.i(TAG, String.format("running command: '%s'", cmd));
|
||||
long startTime = System.nanoTime();
|
||||
Process p = Runtime.getRuntime().exec(cmd);
|
||||
int res;
|
||||
try {
|
||||
res = p.waitFor();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String stdout = inputStreamToString(p.getInputStream());
|
||||
String stderr = inputStreamToString(p.getErrorStream());
|
||||
long elapsedTime = System.nanoTime() - startTime;
|
||||
Log.i(TAG, String.format("ran command: '%s' in %d ms with return code %d", cmd,
|
||||
TimeUnit.NANOSECONDS.toMillis(elapsedTime), res));
|
||||
Log.i(TAG, "stdout");
|
||||
Log.i(TAG, stdout);
|
||||
Log.i(TAG, "stderr");
|
||||
Log.i(TAG, stderr);
|
||||
if (res != 0) {
|
||||
throw new RuntimeException(String.format("failed command: '%s'", cmd));
|
||||
}
|
||||
return stdout;
|
||||
}
|
||||
|
||||
// Run the command and return the stdout split by lines.
|
||||
private static String[] runShellCommandSplitLines(String cmd) throws IOException {
|
||||
return runShellCommand(cmd).split("\n");
|
||||
}
|
||||
|
||||
// Return the compiler filter of a package.
|
||||
private static String getCompilerFilter(String pkg) throws IOException {
|
||||
String cmd = String.format("dumpsys package %s", pkg);
|
||||
String[] lines = runShellCommandSplitLines(cmd);
|
||||
final String substr = "compilation_filter=";
|
||||
for (String line : lines) {
|
||||
int startIndex = line.indexOf(substr);
|
||||
if (startIndex < 0) {
|
||||
continue;
|
||||
}
|
||||
startIndex += substr.length();
|
||||
int endIndex = line.indexOf(']', startIndex);
|
||||
return line.substring(startIndex, endIndex);
|
||||
}
|
||||
throw new RuntimeException("Couldn't find compiler filter in dumpsys package");
|
||||
}
|
||||
|
||||
// Return the number of bytes available in the data partition.
|
||||
private static long getDataDirUsableSpace() {
|
||||
return Environment.getDataDirectory().getUsableSpace();
|
||||
}
|
||||
|
||||
// Fill up the storage until there are bytesRemaining number of bytes available in the data
|
||||
// partition. Writes to the current package's data directory.
|
||||
private void fillUpStorage(long bytesRemaining) throws IOException {
|
||||
Log.i(TAG, String.format("Filling up storage with %d bytes remaining", bytesRemaining));
|
||||
logSpaceRemaining();
|
||||
long numBytesToAdd = getDataDirUsableSpace() - bytesRemaining;
|
||||
String cmd = String.format("fallocate -l %d %s", numBytesToAdd, mBigFile.getAbsolutePath());
|
||||
runShellCommand(cmd);
|
||||
logSpaceRemaining();
|
||||
}
|
||||
|
||||
// Fill up storage so that device is in low storage condition.
|
||||
private void fillUpToLowStorage() throws IOException {
|
||||
fillUpStorage((long) (getStorageLowBytes() * LOW_STORAGE_MULTIPLIER));
|
||||
}
|
||||
|
||||
// TODO(aeubanks): figure out how to get scheduled bg-dexopt to run
|
||||
private static void runBackgroundDexOpt() throws IOException {
|
||||
runShellCommand("cmd package bg-dexopt-job " + PACKAGE_NAME);
|
||||
}
|
||||
|
||||
// Set the time ahead of the last use time of the test app in days.
|
||||
private static void setTimeFutureDays(long futureDays) {
|
||||
setTimeFutureMillis(TimeUnit.DAYS.toMillis(futureDays));
|
||||
}
|
||||
|
||||
// Set the time ahead of the last use time of the test app in milliseconds.
|
||||
private static void setTimeFutureMillis(long futureMillis) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
setTime(currentTime + futureMillis);
|
||||
}
|
||||
|
||||
private static void setTime(long time) {
|
||||
AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
|
||||
am.setTime(time);
|
||||
}
|
||||
|
||||
// Return the number of free bytes when the data partition is considered low on storage.
|
||||
private static long getStorageLowBytes() {
|
||||
StorageManager storageManager = (StorageManager) getContext().getSystemService(
|
||||
Context.STORAGE_SERVICE);
|
||||
return storageManager.getStorageLowBytes(Environment.getDataDirectory());
|
||||
}
|
||||
|
||||
// Log the amount of space remaining in the data directory.
|
||||
private static void logSpaceRemaining() throws IOException {
|
||||
runShellCommand("df -h /data");
|
||||
}
|
||||
|
||||
// Compile the given package with the given compiler filter.
|
||||
private static void compilePackageWithFilter(String pkg, String filter) throws IOException {
|
||||
runShellCommand(String.format("cmd package compile -f -m %s %s", filter, pkg));
|
||||
}
|
||||
|
||||
// Test that background dexopt under normal conditions succeeds.
|
||||
@Test
|
||||
public void testBackgroundDexOpt() throws IOException {
|
||||
// Set filter to quicken.
|
||||
compilePackageWithFilter(PACKAGE_NAME, "verify");
|
||||
Assert.assertEquals("verify", getCompilerFilter(PACKAGE_NAME));
|
||||
|
||||
runBackgroundDexOpt();
|
||||
|
||||
// Verify that bg-dexopt is successful.
|
||||
Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME));
|
||||
}
|
||||
|
||||
// Test that background dexopt under low storage conditions upgrades used packages.
|
||||
@Test
|
||||
public void testBackgroundDexOptDowngradeSkipRecentlyUsedPackage() throws IOException {
|
||||
// Should be less than DOWNGRADE_AFTER_DAYS.
|
||||
long deltaDays = DOWNGRADE_AFTER_DAYS - 1;
|
||||
try {
|
||||
// Set time to future.
|
||||
setTimeFutureDays(deltaDays);
|
||||
|
||||
// Set filter to quicken.
|
||||
compilePackageWithFilter(PACKAGE_NAME, "quicken");
|
||||
Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME));
|
||||
|
||||
// Fill up storage to trigger low storage threshold.
|
||||
fillUpToLowStorage();
|
||||
|
||||
runBackgroundDexOpt();
|
||||
|
||||
// Verify that downgrade did not happen.
|
||||
Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME));
|
||||
} finally {
|
||||
// Reset time.
|
||||
setTimeFutureDays(-deltaDays);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that background dexopt under low storage conditions downgrades unused packages.
|
||||
@Test
|
||||
public void testBackgroundDexOptDowngradeSuccessful() throws IOException {
|
||||
// Should be more than DOWNGRADE_AFTER_DAYS.
|
||||
long deltaDays = DOWNGRADE_AFTER_DAYS + 1;
|
||||
try {
|
||||
// Set time to future.
|
||||
setTimeFutureDays(deltaDays);
|
||||
|
||||
// Set filter to quicken.
|
||||
compilePackageWithFilter(PACKAGE_NAME, "quicken");
|
||||
Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME));
|
||||
|
||||
// Fill up storage to trigger low storage threshold.
|
||||
fillUpToLowStorage();
|
||||
|
||||
runBackgroundDexOpt();
|
||||
|
||||
// Verify that downgrade is successful.
|
||||
Assert.assertEquals(DOWNGRADE_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME));
|
||||
} finally {
|
||||
// Reset time.
|
||||
setTimeFutureDays(-deltaDays);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user