Merge "iorap_functional_test: Add iorap function test." into rvc-dev am: d3e541eb03 am: 77bf81a749 am: 54b18d3205
Change-Id: Iad467e4fe9883fdaa7bd085e2493999a7f9204e0
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
android_test {
|
android_test {
|
||||||
name: "iorap-functional-tests",
|
name: "iorap-functional-tests",
|
||||||
srcs: ["src/**/*.java"],
|
srcs: ["src/**/*.java"],
|
||||||
|
data: ["test_data/*"],
|
||||||
static_libs: [
|
static_libs: [
|
||||||
// Non-test dependencies
|
// Non-test dependencies
|
||||||
// library under test
|
// library under test
|
||||||
|
|||||||
@@ -45,6 +45,20 @@
|
|||||||
<option name="run-command" value="sleep 1" />
|
<option name="run-command" value="sleep 1" />
|
||||||
</target_preparer>
|
</target_preparer>
|
||||||
|
|
||||||
|
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
|
||||||
|
<option name="cleanup" value="true" />
|
||||||
|
<option name="abort-on-push-failure" value="true" />
|
||||||
|
<option name="push-file"
|
||||||
|
key="iorap_test_app_v1.apk"
|
||||||
|
value="/data/misc/iorapd/iorap_test_app_v1.apk" />
|
||||||
|
<option name="push-file"
|
||||||
|
key="iorap_test_app_v2.apk"
|
||||||
|
value="/data/misc/iorapd/iorap_test_app_v2.apk" />
|
||||||
|
<option name="push-file"
|
||||||
|
key="iorap_test_app_v3.apk"
|
||||||
|
value="/data/misc/iorapd/iorap_test_app_v3.apk" />
|
||||||
|
</target_preparer>
|
||||||
|
|
||||||
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
|
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
|
||||||
<option name="package" value="com.google.android.startop.iorap.tests" />
|
<option name="package" value="com.google.android.startop.iorap.tests" />
|
||||||
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
|
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
|
||||||
|
|||||||
@@ -37,45 +37,46 @@ import androidx.test.uiautomator.By;
|
|||||||
import androidx.test.uiautomator.UiDevice;
|
import androidx.test.uiautomator.UiDevice;
|
||||||
import androidx.test.uiautomator.Until;
|
import androidx.test.uiautomator.Until;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for the work flow of iorap.
|
* Test for the work flow of iorap.
|
||||||
*
|
*
|
||||||
* <p> This test tests the function of iorap from perfetto collection -> compilation ->
|
* <p> This test tests the function of iorap from:
|
||||||
* prefetching.
|
* perfetto collection -> compilation -> prefetching -> version update -> perfetto collection.
|
||||||
* </p>
|
|
||||||
*/
|
*/
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class IorapWorkFlowTest {
|
public class IorapWorkFlowTest {
|
||||||
|
|
||||||
private static final String TAG = "IorapWorkFlowTest";
|
private static final String TAG = "IorapWorkFlowTest";
|
||||||
|
|
||||||
private static final String TEST_PACKAGE_NAME = "com.android.settings";
|
private static final String TEST_APP_VERSION_ONE_PATH = "/data/misc/iorapd/iorap_test_app_v1.apk";
|
||||||
private static final String TEST_ACTIVITY_NAME = "com.android.settings.Settings";
|
private static final String TEST_APP_VERSION_TWO_PATH = "/data/misc/iorapd/iorap_test_app_v2.apk";
|
||||||
|
private static final String TEST_APP_VERSION_THREE_PATH = "/data/misc/iorapd/iorap_test_app_v3.apk";
|
||||||
|
|
||||||
private static final String DB_PATH = "/data/misc/iorapd/sqlite.db";
|
private static final String DB_PATH = "/data/misc/iorapd/sqlite.db";
|
||||||
private static final Duration TIMEOUT = Duration.ofSeconds(300L);
|
private static final Duration TIMEOUT = Duration.ofSeconds(300L);
|
||||||
|
|
||||||
private static final String READAHEAD_INDICATOR =
|
|
||||||
"Description = /data/misc/iorapd/com.android.settings/-?\\d+/com.android.settings.Settings/compiled_traces/compiled_trace.pb";
|
|
||||||
|
|
||||||
private UiDevice mDevice;
|
private UiDevice mDevice;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void startMainActivityFromHomeScreen() throws Exception {
|
public void setUp() throws Exception {
|
||||||
// Initialize UiDevice instance
|
// Initialize UiDevice instance
|
||||||
mDevice = UiDevice.getInstance(getInstrumentation());
|
mDevice = UiDevice.getInstance(getInstrumentation());
|
||||||
|
|
||||||
@@ -88,21 +89,81 @@ public class IorapWorkFlowTest {
|
|||||||
mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), TIMEOUT.getSeconds());
|
mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), TIMEOUT.getSeconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
String packageName = "com.example.ioraptestapp";
|
||||||
|
uninstallApk(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
@Test (timeout = 300000)
|
@Test (timeout = 300000)
|
||||||
public void testApp() throws Exception {
|
public void testNormalWorkFlow() throws Exception {
|
||||||
assertThat(mDevice, notNullValue());
|
assertThat(mDevice, notNullValue());
|
||||||
|
|
||||||
|
// Install test app version one
|
||||||
|
installApk(TEST_APP_VERSION_ONE_PATH);
|
||||||
|
String packageName = "com.example.ioraptestapp";
|
||||||
|
String activityName = "com.example.ioraptestapp.MainActivity";
|
||||||
|
|
||||||
// Perfetto trace collection phase.
|
// Perfetto trace collection phase.
|
||||||
assertTrue(startAppForPerfettoTrace(/*expectPerfettoTraceCount=*/1));
|
assertTrue(startAppForPerfettoTrace(
|
||||||
assertTrue(startAppForPerfettoTrace(/*expectPerfettoTraceCount=*/2));
|
packageName, activityName, /*version=*/1L));
|
||||||
assertTrue(startAppForPerfettoTrace(/*expectPerfettoTraceCount=*/3));
|
assertTrue(startAppForPerfettoTrace(
|
||||||
assertTrue(checkPerfettoTracesExistence(TIMEOUT, 3));
|
packageName, activityName, /*version=*/1L));
|
||||||
|
assertTrue(startAppForPerfettoTrace(
|
||||||
|
packageName, activityName, /*version=*/1L));
|
||||||
|
|
||||||
// Trigger maintenance service for compilation.
|
// Trigger maintenance service for compilation.
|
||||||
assertTrue(compile(TIMEOUT));
|
TimeUnit.SECONDS.sleep(5L);
|
||||||
|
assertTrue(compile(packageName, activityName, /*version=*/1L));
|
||||||
|
|
||||||
// Check if prefetching works.
|
// Run app with prefetching
|
||||||
assertTrue(waitForPrefetchingFromLogcat(/*expectPerfettoTraceCount=*/3));
|
assertTrue(startAppWithCompiledTrace(
|
||||||
|
packageName, activityName, /*version=*/1L));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test (timeout = 300000)
|
||||||
|
public void testUpdateApp() throws Exception {
|
||||||
|
assertThat(mDevice, notNullValue());
|
||||||
|
|
||||||
|
// Install test app version two,
|
||||||
|
String packageName = "com.example.ioraptestapp";
|
||||||
|
String activityName = "com.example.ioraptestapp.MainActivity";
|
||||||
|
installApk(TEST_APP_VERSION_TWO_PATH);
|
||||||
|
|
||||||
|
// Perfetto trace collection phase.
|
||||||
|
assertTrue(startAppForPerfettoTrace(
|
||||||
|
packageName, activityName, /*version=*/2L));
|
||||||
|
assertTrue(startAppForPerfettoTrace(
|
||||||
|
packageName, activityName, /*version=*/2L));
|
||||||
|
assertTrue(startAppForPerfettoTrace(
|
||||||
|
packageName, activityName, /*version=*/2L));
|
||||||
|
|
||||||
|
// Trigger maintenance service for compilation.
|
||||||
|
TimeUnit.SECONDS.sleep(5L);
|
||||||
|
assertTrue(compile(packageName, activityName, /*version=*/2L));
|
||||||
|
|
||||||
|
// Run app with prefetching
|
||||||
|
assertTrue(startAppWithCompiledTrace(
|
||||||
|
packageName, activityName, /*version=*/2L));
|
||||||
|
|
||||||
|
// Update test app to version 3
|
||||||
|
installApk(TEST_APP_VERSION_THREE_PATH);
|
||||||
|
|
||||||
|
// Rerun app, should do pefetto tracing.
|
||||||
|
assertTrue(startAppForPerfettoTrace(
|
||||||
|
packageName, activityName, /*version=*/3L));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void installApk(String apkPath) throws Exception {
|
||||||
|
// Disable the selinux to allow pm install apk in the dir.
|
||||||
|
executeShellCommand("setenforce 0");
|
||||||
|
executeShellCommand("pm install -r -d " + apkPath);
|
||||||
|
executeShellCommand("setenforce 1");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void uninstallApk(String apkPath) throws Exception {
|
||||||
|
executeShellCommand("pm uninstall " + apkPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -110,43 +171,81 @@ public class IorapWorkFlowTest {
|
|||||||
*
|
*
|
||||||
* @param expectPerfettoTraceCount is the expected count of perfetto traces.
|
* @param expectPerfettoTraceCount is the expected count of perfetto traces.
|
||||||
*/
|
*/
|
||||||
private boolean startAppForPerfettoTrace(long expectPerfettoTraceCount)
|
private boolean startAppForPerfettoTrace(
|
||||||
|
String packageName, String activityName, long version)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
// Close the specified app if it's open
|
LogcatTimestamp timestamp = runAppOnce(packageName, activityName);
|
||||||
closeApp();
|
return waitForPerfettoTraceSavedFromLogcat(
|
||||||
// Launch the specified app
|
packageName, activityName, version, timestamp);
|
||||||
startApp();
|
}
|
||||||
// Wait for the app to appear
|
|
||||||
mDevice.wait(Until.hasObject(By.pkg(TEST_PACKAGE_NAME).depth(0)), TIMEOUT.getSeconds());
|
|
||||||
|
|
||||||
String sql = "SELECT COUNT(*) FROM activities "
|
private boolean startAppWithCompiledTrace(
|
||||||
+ "JOIN app_launch_histories ON activities.id = app_launch_histories.activity_id "
|
String packageName, String activityName, long version)
|
||||||
+ "JOIN raw_traces ON raw_traces.history_id = app_launch_histories.id "
|
throws Exception {
|
||||||
+ "WHERE activities.name = ?";
|
LogcatTimestamp timestamp = runAppOnce(packageName, activityName);
|
||||||
return checkAndWaitEntriesNum(sql, new String[]{TEST_ACTIVITY_NAME}, expectPerfettoTraceCount,
|
return waitForPrefetchingFromLogcat(
|
||||||
TIMEOUT);
|
packageName, activityName, version, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LogcatTimestamp runAppOnce(String packageName, String activityName) throws Exception {
|
||||||
|
// Close the specified app if it's open
|
||||||
|
closeApp(packageName);
|
||||||
|
LogcatTimestamp timestamp = new LogcatTimestamp();
|
||||||
|
// Launch the specified app
|
||||||
|
startApp(packageName, activityName);
|
||||||
|
// Wait for the app to appear
|
||||||
|
mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), TIMEOUT.getSeconds());
|
||||||
|
return timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invokes the maintenance to compile the perfetto traces to compiled trace.
|
// Invokes the maintenance to compile the perfetto traces to compiled trace.
|
||||||
private boolean compile(Duration timeout) throws Exception {
|
private boolean compile(
|
||||||
|
String packageName, String activityName, long version) throws Exception {
|
||||||
// The job id (283673059) is defined in class IorapForwardingService.
|
// The job id (283673059) is defined in class IorapForwardingService.
|
||||||
executeShellCommand("cmd jobscheduler run -f android 283673059");
|
executeShellCommandViaTmpFile("cmd jobscheduler run -f android 283673059");
|
||||||
|
return waitForFileExistence(getCompiledTracePath(packageName, activityName, version));
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for the compilation.
|
private String getCompiledTracePath(
|
||||||
String sql = "SELECT COUNT(*) FROM activities JOIN prefetch_files ON "
|
String packageName, String activityName, long version) {
|
||||||
+ "activities.id = prefetch_files.activity_id "
|
return String.format(
|
||||||
+ "WHERE activities.name = ?";
|
"/data/misc/iorapd/%s/%d/%s/compiled_traces/compiled_trace.pb",
|
||||||
boolean result = checkAndWaitEntriesNum(sql, new String[]{TEST_ACTIVITY_NAME}, /*count=*/1,
|
packageName, version, activityName);
|
||||||
timeout);
|
}
|
||||||
if (!result) {
|
|
||||||
return false;
|
/**
|
||||||
|
* Starts the testing app.
|
||||||
|
*/
|
||||||
|
private void startApp(String packageName, String activityName) throws Exception {
|
||||||
|
executeShellCommandViaTmpFile(
|
||||||
|
String.format("am start %s/%s", packageName, activityName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the testing app.
|
||||||
|
* <p> Keep trying to kill the process of the app until no process of the app package
|
||||||
|
* appears.</p>
|
||||||
|
*/
|
||||||
|
private void closeApp(String packageName) throws Exception {
|
||||||
|
while (true) {
|
||||||
|
String pid = executeShellCommand("pidof " + packageName);
|
||||||
|
if (pid.isEmpty()) {
|
||||||
|
Log.i(TAG, "Closed app " + packageName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
executeShellCommand("kill -9 " + pid);
|
||||||
|
TimeUnit.SECONDS.sleep(1L);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return retryWithTimeout(timeout, () -> {
|
/** Waits for a file to appear. */
|
||||||
|
private boolean waitForFileExistence(String fileName) throws Exception {
|
||||||
|
return retryWithTimeout(TIMEOUT, () -> {
|
||||||
try {
|
try {
|
||||||
String compiledTrace = getCompiledTraceFilePath();
|
String fileExists = executeShellCommandViaTmpFile(
|
||||||
File compiledTraceLocal = copyFileToLocal(compiledTrace, "compiled_trace.tmp");
|
String.format("test -f %s; echo $?", fileName));
|
||||||
return compiledTraceLocal.exists();
|
Log.i(TAG, fileName + " existence is " + fileExists);
|
||||||
|
return fileExists.trim().equals("0");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.i(TAG, e.getMessage());
|
Log.i(TAG, e.getMessage());
|
||||||
return false;
|
return false;
|
||||||
@@ -154,92 +253,96 @@ public class IorapWorkFlowTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Waits for the perfetto trace saved message from logcat. */
|
||||||
* Check if all the perfetto traces in the db exist.
|
private boolean waitForPerfettoTraceSavedFromLogcat(
|
||||||
*/
|
String packageName, String activityName, long version, LogcatTimestamp timestamp)
|
||||||
private boolean checkPerfettoTracesExistence(Duration timeout, int expectPerfettoTraceCount)
|
|
||||||
throws Exception {
|
throws Exception {
|
||||||
return retryWithTimeout(timeout, () -> {
|
Pattern p = Pattern.compile(".*"
|
||||||
try {
|
+ getPerfettoTraceSavedIndicator(packageName, activityName, version)
|
||||||
File dbFile = getIorapDb();
|
+ "(.*[.]perfetto_trace[.]pb)\n.*", Pattern.DOTALL);
|
||||||
List<String> traces = getPerfettoTracePaths(dbFile);
|
|
||||||
assertEquals(traces.size(), expectPerfettoTraceCount);
|
|
||||||
|
|
||||||
int count = 0;
|
return retryWithTimeout(TIMEOUT, () -> {
|
||||||
for (String trace : traces) {
|
try {
|
||||||
File tmp = copyFileToLocal(trace, "perfetto_trace.tmp" + count);
|
String log = timestamp.getLogcatAfter();
|
||||||
++count;
|
Matcher m = p.matcher(log);
|
||||||
Log.i(TAG, "Check perfetto trace: " + trace);
|
Log.d(TAG, "Tries to find perfetto trace...");
|
||||||
if (!tmp.exists()) {
|
if (!m.matches()) {
|
||||||
Log.i(TAG, "Perfetto trace does not exist: " + trace);
|
Log.i(TAG, "Cannot find perfetto trace saved in log.");
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
String filePath = m.group(1);
|
||||||
|
Log.i(TAG, "Perfetto trace is saved to " + filePath);
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch(Exception e) {
|
||||||
Log.i(TAG, e.getMessage());
|
Log.e(TAG, e.getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPerfettoTraceSavedIndicator(
|
||||||
|
String packageName, String activityName, long version) {
|
||||||
|
return String.format(
|
||||||
|
"Perfetto TraceBuffer saved to file: /data/misc/iorapd/%s/%d/%s/raw_traces/",
|
||||||
|
packageName, version, activityName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the perfetto traces file path from the db.
|
* Waits for the prefetching log in the logcat.
|
||||||
*/
|
|
||||||
private List<String> getPerfettoTracePaths(File dbFile) throws Exception {
|
|
||||||
String sql = "SELECT raw_traces.file_path FROM activities "
|
|
||||||
+ "JOIN app_launch_histories ON activities.id = app_launch_histories.activity_id "
|
|
||||||
+ "JOIN raw_traces ON raw_traces.history_id = app_launch_histories.id "
|
|
||||||
+ "WHERE activities.name = ?";
|
|
||||||
|
|
||||||
List<String> perfettoTraces = new ArrayList<>();
|
|
||||||
try (SQLiteDatabase db = SQLiteDatabase
|
|
||||||
.openDatabase(dbFile.getPath(), null, SQLiteDatabase.OPEN_READONLY)) {
|
|
||||||
Cursor cursor = db.rawQuery(sql, new String[]{TEST_ACTIVITY_NAME});
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
perfettoTraces.add(cursor.getString(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return perfettoTraces;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCompiledTraceFilePath() throws Exception {
|
|
||||||
File dbFile = getIorapDb();
|
|
||||||
try (SQLiteDatabase db = SQLiteDatabase
|
|
||||||
.openDatabase(dbFile.getPath(), null, SQLiteDatabase.OPEN_READONLY)) {
|
|
||||||
String sql = "SELECT prefetch_files.file_path FROM activities JOIN prefetch_files ON "
|
|
||||||
+ "activities.id = prefetch_files.activity_id "
|
|
||||||
+ "WHERE activities.name = ?";
|
|
||||||
return DatabaseUtils.stringForQuery(db, sql, new String[]{TEST_ACTIVITY_NAME});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the number of entries in the database table.
|
|
||||||
*
|
*
|
||||||
* <p> Keep checking until the timeout.
|
* <p> When prefetching works, the perfetto traces should not be collected. </p>
|
||||||
*/
|
*/
|
||||||
private boolean checkAndWaitEntriesNum(String sql, String[] selectionArgs, long count,
|
private boolean waitForPrefetchingFromLogcat(
|
||||||
Duration timeout)
|
String packageName, String activityName, long version, LogcatTimestamp timestamp)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
return retryWithTimeout(timeout, () -> {
|
Pattern p = Pattern.compile(
|
||||||
|
".*" + getReadaheadIndicator(packageName, activityName, version) +
|
||||||
|
".*Total File Paths=(\\d+) \\(good: (\\d+[.]?\\d*)%\\)\n"
|
||||||
|
+ ".*Total Entries=(\\d+) \\(good: (\\d+[.]?\\d*)%\\)\n"
|
||||||
|
+ ".*Total Bytes=(\\d+) \\(good: (\\d+[.]?\\d*)%\\).*",
|
||||||
|
Pattern.DOTALL);
|
||||||
|
|
||||||
|
return retryWithTimeout(TIMEOUT, () -> {
|
||||||
try {
|
try {
|
||||||
File db = getIorapDb();
|
String log = timestamp.getLogcatAfter();
|
||||||
long curCount = getEntriesNum(db, selectionArgs, sql);
|
Matcher m = p.matcher(log);
|
||||||
Log.i(TAG, String
|
if (!m.matches()) {
|
||||||
.format("For %s, current count is %d, expected count is :%d.", sql, curCount,
|
Log.i(TAG, "Cannot find readahead log.");
|
||||||
count));
|
return false;
|
||||||
return curCount == count;
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
Log.i(TAG, e.getMessage());
|
int totalFilePath = Integer.parseInt(m.group(1));
|
||||||
|
float totalFilePathGoodRate = Float.parseFloat(m.group(2)) / 100;
|
||||||
|
int totalEntries = Integer.parseInt(m.group(3));
|
||||||
|
float totalEntriesGoodRate = Float.parseFloat(m.group(4)) / 100;
|
||||||
|
int totalBytes = Integer.parseInt(m.group(5));
|
||||||
|
float totalBytesGoodRate = Float.parseFloat(m.group(6)) / 100;
|
||||||
|
|
||||||
|
Log.i(TAG, String.format(
|
||||||
|
"totalFilePath: %d (good %.2f) totalEntries: %d (good %.2f) totalBytes: %d (good %.2f)",
|
||||||
|
totalFilePath, totalFilePathGoodRate, totalEntries, totalEntriesGoodRate, totalBytes,
|
||||||
|
totalBytesGoodRate));
|
||||||
|
|
||||||
|
return totalFilePath > 0 &&
|
||||||
|
totalEntries > 0 &&
|
||||||
|
totalBytes > 0 &&
|
||||||
|
totalFilePathGoodRate > 0.5 &&
|
||||||
|
totalEntriesGoodRate > 0.5 &&
|
||||||
|
totalBytesGoodRate > 0.5;
|
||||||
|
} catch(Exception e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static String getReadaheadIndicator(
|
||||||
* Retry until timeout.
|
String packageName, String activityName, long version) {
|
||||||
*/
|
return String.format(
|
||||||
|
"Description = /data/misc/iorapd/%s/%d/%s/compiled_traces/compiled_trace.pb",
|
||||||
|
packageName, version, activityName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Retry until timeout. */
|
||||||
private boolean retryWithTimeout(Duration timeout, BooleanSupplier supplier) throws Exception {
|
private boolean retryWithTimeout(Duration timeout, BooleanSupplier supplier) throws Exception {
|
||||||
long totalSleepTimeSeconds = 0L;
|
long totalSleepTimeSeconds = 0L;
|
||||||
long sleepIntervalSeconds = 2L;
|
long sleepIntervalSeconds = 2L;
|
||||||
@@ -256,112 +359,27 @@ public class IorapWorkFlowTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the number of entries in the query of sql.
|
* Executes command in adb shell via a tmp file.
|
||||||
*/
|
|
||||||
private long getEntriesNum(File dbFile, String[] selectionArgs, String sql) throws Exception {
|
|
||||||
try (SQLiteDatabase db = SQLiteDatabase
|
|
||||||
.openDatabase(dbFile.getPath(), null, SQLiteDatabase.OPEN_READONLY)) {
|
|
||||||
return DatabaseUtils.longForQuery(db, sql, selectionArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the iorapd sqlite db file.
|
|
||||||
*
|
*
|
||||||
* <p> The test cannot access the db file directly under "/data/misc/iorapd".
|
* <p> This should be run as root.</p>
|
||||||
* Copy it to the local directory and change the mode.
|
|
||||||
*/
|
*/
|
||||||
private File getIorapDb() throws Exception {
|
private static String executeShellCommandViaTmpFile(String cmd) throws Exception {
|
||||||
File tmpDb = copyFileToLocal("/data/misc/iorapd/sqlite.db", "tmp.db");
|
Log.i(TAG, "Execute via tmp file: " + cmd);
|
||||||
// Change the mode of the file to allow the access from test.
|
Path tmp = null;
|
||||||
executeShellCommand("chmod 777 " + tmpDb.getPath());
|
try {
|
||||||
return tmpDb;
|
tmp = Files.createTempFile(/*prefix=*/null, /*suffix=*/".sh");
|
||||||
}
|
Files.write(tmp, cmd.getBytes(StandardCharsets.UTF_8));
|
||||||
|
tmp.toFile().setExecutable(true);
|
||||||
/**
|
return UiDevice.getInstance(
|
||||||
* Copys a file to local directory.
|
InstrumentationRegistry.getInstrumentation()).
|
||||||
*/
|
executeShellCommand(tmp.toString());
|
||||||
private File copyFileToLocal(String src, String tgtFileName) throws Exception {
|
} finally {
|
||||||
File localDir = getApplicationContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
|
if (tmp != null) {
|
||||||
File localFile = new File(localDir, tgtFileName);
|
Files.delete(tmp);
|
||||||
executeShellCommand(String.format("cp %s %s", src, localFile.getPath()));
|
|
||||||
return localFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the testing app.
|
|
||||||
*/
|
|
||||||
private void startApp() throws Exception {
|
|
||||||
Context context = getApplicationContext();
|
|
||||||
final Intent intent = context.getPackageManager()
|
|
||||||
.getLaunchIntentForPackage(TEST_PACKAGE_NAME);
|
|
||||||
context.startActivity(intent);
|
|
||||||
Log.i(TAG, "Started app " + TEST_PACKAGE_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the testing app.
|
|
||||||
* <p> Keep trying to kill the process of the app until no process of the app package
|
|
||||||
* appears.</p>
|
|
||||||
*/
|
|
||||||
private void closeApp() throws Exception {
|
|
||||||
while (true) {
|
|
||||||
String pid = executeShellCommand("pidof " + TEST_PACKAGE_NAME);
|
|
||||||
if (pid.isEmpty()) {
|
|
||||||
Log.i(TAG, "Closed app " + TEST_PACKAGE_NAME);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
executeShellCommand("kill -9 " + pid);
|
|
||||||
TimeUnit.SECONDS.sleep(1L);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Waits for the prefetching log in the logcat.
|
|
||||||
*
|
|
||||||
* <p> When prefetching works, the perfetto traces should not be collected. </p>
|
|
||||||
*/
|
|
||||||
private boolean waitForPrefetchingFromLogcat(long expectPerfettoTraceCount) throws Exception {
|
|
||||||
if (!startAppForPerfettoTrace(expectPerfettoTraceCount)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String log = executeShellCommand("logcat -d");
|
|
||||||
|
|
||||||
Pattern p = Pattern.compile(
|
|
||||||
".*" + READAHEAD_INDICATOR
|
|
||||||
+ ".*Total File Paths=(\\d+) \\(good: (\\d+[.]?\\d*)%\\)\n"
|
|
||||||
+ ".*Total Entries=(\\d+) \\(good: (\\d+[.]?\\d*)%\\)\n"
|
|
||||||
+ ".*Total Bytes=(\\d+) \\(good: (\\d+[.]?\\d*)%\\).*",
|
|
||||||
Pattern.DOTALL);
|
|
||||||
Matcher m = p.matcher(log);
|
|
||||||
|
|
||||||
if (!m.matches()) {
|
|
||||||
Log.i(TAG, "Cannot find readahead log.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int totalFilePath = Integer.parseInt(m.group(1));
|
|
||||||
float totalFilePathGoodRate = Float.parseFloat(m.group(2)) / 100;
|
|
||||||
int totalEntries = Integer.parseInt(m.group(3));
|
|
||||||
float totalEntriesGoodRate = Float.parseFloat(m.group(4)) / 100;
|
|
||||||
int totalBytes = Integer.parseInt(m.group(5));
|
|
||||||
float totalBytesGoodRate = Float.parseFloat(m.group(6)) / 100;
|
|
||||||
|
|
||||||
Log.i(TAG, String.format(
|
|
||||||
"totalFilePath: %d (good %.2f) totalEntries: %d (good %.2f) totalBytes: %d (good %.2f)",
|
|
||||||
totalFilePath, totalFilePathGoodRate, totalEntries, totalEntriesGoodRate, totalBytes,
|
|
||||||
totalBytesGoodRate));
|
|
||||||
|
|
||||||
return totalFilePath > 0 &&
|
|
||||||
totalEntries > 0 &&
|
|
||||||
totalBytes > 100000 &&
|
|
||||||
totalFilePathGoodRate > 0.5 &&
|
|
||||||
totalEntriesGoodRate > 0.5 &&
|
|
||||||
totalBytesGoodRate > 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes command in adb shell.
|
* Executes command in adb shell.
|
||||||
*
|
*
|
||||||
@@ -372,6 +390,27 @@ public class IorapWorkFlowTest {
|
|||||||
return UiDevice.getInstance(
|
return UiDevice.getInstance(
|
||||||
InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
|
InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class LogcatTimestamp {
|
||||||
|
private String epochTime;
|
||||||
|
|
||||||
|
public LogcatTimestamp() throws Exception{
|
||||||
|
long currentTimeMillis = System.currentTimeMillis();
|
||||||
|
epochTime = String.format(
|
||||||
|
"%d.%d", currentTimeMillis/1000, currentTimeMillis%1000);
|
||||||
|
Log.i(TAG, "Current logcat timestamp is " + epochTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For example, 1585264100.000
|
||||||
|
public String getEpochTime() {
|
||||||
|
return epochTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the logcat after this epoch time.
|
||||||
|
public String getLogcatAfter() throws Exception {
|
||||||
|
return executeShellCommandViaTmpFile(
|
||||||
|
"logcat -v epoch -t '" + epochTime + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
startop/iorap/functional_tests/test_data/iorap_test_app_v1.apk
Symbolic link
1
startop/iorap/functional_tests/test_data/iorap_test_app_v1.apk
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../../../../packages/modules/ArtPrebuilt/iorap/test/iorap_test_app_v1.apk
|
||||||
1
startop/iorap/functional_tests/test_data/iorap_test_app_v2.apk
Symbolic link
1
startop/iorap/functional_tests/test_data/iorap_test_app_v2.apk
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../../../../packages/modules/ArtPrebuilt/iorap/test/iorap_test_app_v2.apk
|
||||||
1
startop/iorap/functional_tests/test_data/iorap_test_app_v3.apk
Symbolic link
1
startop/iorap/functional_tests/test_data/iorap_test_app_v3.apk
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../../../../packages/modules/ArtPrebuilt/iorap/test/iorap_test_app_v3.apk
|
||||||
Reference in New Issue
Block a user