/*
* Copyright (C) 2012 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.framework.tests;
import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner;
import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.CollectingTestListener;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.TestResult;
import com.android.tradefed.testtype.IDeviceTest;
import com.android.tradefed.testtype.IRemoteTest;
import com.android.tradefed.util.IRunUtil;
import com.android.tradefed.util.RunUtil;
import com.google.common.collect.ImmutableMap;
import junit.framework.Assert;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Test that measures the average latency of foreground and background
* operations in various scenarios.
*/
public class FrameworkPerfTest implements IRemoteTest, IDeviceTest {
private static final String TEST_PACKAGE_NAME = "com.android.frameworkperf";
private static final String TEST_RUNNER_NAME = "android.test.InstrumentationTestRunner";
private static final Pattern METRICS_PATTERN =
Pattern.compile("(\\d+\\.\\d+),(\\d+),(\\d+),(\\d+\\.\\d+),(\\d+),(\\d+)");
private static final int PERF_TIMEOUT = 30 * 60 * 1000; //30 minutes timeout
private static final int PRE_TEST_SLEEP_MS = 30 *1000; //30s sleep prior to test start
private static final String LAYOUT = "framework_perf_layout";
private static final String SCHEDULING = "framework_perf_scheduling";
private static final String METHOD = "framework_perf_method";
private static final String GC = "framework_perf_gc";
private static final String IPCFG = "framework_perf_ipcfg";
private static final String XML = "framework_perf_xml";
private static final String BITMAP = "framework_perf_bitmap";
private static final String FILE = "framework_perf_file";
private static final String OTHER = "framework_perf_other";
private static final ImmutableMap<String, String> TEST_TAG_MAP =
new ImmutableMap.Builder<String, String>()
.put("LayoutInflaterButtonFg", LAYOUT)
.put("LayoutInflaterFg", LAYOUT)
.put("LayoutInflaterImageButtonFg", LAYOUT)
.put("LayoutInflaterLargeFg", LAYOUT)
.put("LayoutInflaterViewFg", LAYOUT)
.put("SchedFgSchedBg", SCHEDULING)
.put("MethodCallFgCPUBg", METHOD)
.put("MethodCallFgCreateFileBg", METHOD)
.put("MethodCallFgCreateWriteFileBg", METHOD)
.put("MethodCallFgCreateWriteSyncFileBg", METHOD)
.put("MethodCallFgGcBg", METHOD)
.put("MethodCallFgReadFileBg", METHOD)
.put("MethodCallFgSchedBg", METHOD)
.put("MethodCallFgWriteFileBg", METHOD)
.put("MethodCallFg", METHOD)
.put("ObjectGcFg", GC)
.put("FinalizingGcFg", GC)
.put("GcFg", GC)
.put("PaintGcFg", GC)
.put("IpcFgCPUBg", IPCFG)
.put("IpcFgCreateFileBg", IPCFG)
.put("IpcFgCreateWriteFileBg", IPCFG)
.put("IpcFgCreateWriteSyncFileBg", IPCFG)
.put("IpcFgGcBg", IPCFG)
.put("IpcFgReadFileBg", IPCFG)
.put("IpcFgSchedBg", IPCFG)
.put("IpcFgWriteFileBg", IPCFG)
.put("IpcFg", IPCFG)
.put("OpenXmlResFg", XML)
.put("ParseLargeXmlResFg", XML)
.put("ParseXmlResFg", XML)
.put("ReadXmlAttrsFg", XML)
.put("CreateBitmapFg", BITMAP)
.put("CreateRecycleBitmapFg", BITMAP)
.put("LoadLargeBitmapFg", BITMAP)
.put("LoadLargeScaledBitmapFg", BITMAP)
.put("LoadRecycleLargeBitmapFg", BITMAP)
.put("LoadRecycleSmallBitmapFg", BITMAP)
.put("LoadSmallBitmapFg", BITMAP)
.put("LoadSmallScaledBitmapFg", BITMAP)
.put("CreateFileFg", FILE)
.put("CreateWriteFileFg", FILE)
.put("CreateWriteSyncFileFg", FILE)
.put("ReadFileFgCreateWriteFileBg", FILE)
.put("ReadFileFgCreateWriteSyncFileBg", FILE)
.put("ReadFileFgReadFileBg", FILE)
.put("ReadFileFgWriteFileBg", FILE)
.put("ReadFileFg", FILE)
.put("WriteFileFgCreateWriteFileBg", FILE)
.put("WriteFileFgCreateWriteSyncFileBg", FILE)
.put("WriteFileFgReadFileBg", FILE)
.put("WriteFileFgWriteFileBg", FILE)
.put("WriteFileFg", FILE)
.build();
private ITestDevice mTestDevice = null;
/**
* {@inheritDoc}
*/
@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
Assert.assertNotNull(mTestDevice);
getDevice().reboot();
getRunUtil().sleep(PRE_TEST_SLEEP_MS);
IRemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(TEST_PACKAGE_NAME,
TEST_RUNNER_NAME, mTestDevice.getIDevice());
runner.setMaxtimeToOutputResponse(PERF_TIMEOUT);
CollectingTestListener collectingListener = new CollectingTestListener();
Assert.assertTrue(mTestDevice.runInstrumentationTests(runner, collectingListener));
Collection<TestResult> testResultsCollection =
collectingListener.getCurrentRunResults().getTestResults().values();
List<TestResult> testResults =
new ArrayList<TestResult>(testResultsCollection);
if (!testResults.isEmpty()) {
Map<String, String> testMetrics = testResults.get(0).getMetrics();
if (testMetrics != null) {
reportMetrics(listener, testMetrics);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public ITestDevice getDevice() {
return mTestDevice;
}
/**
* {@inheritDoc}
*/
@Override
public void setDevice(ITestDevice device) {
mTestDevice = device;
}
/**
* Report run metrics by creating an empty test run to stick them in.
* @param listener The {@link ITestInvocationListener} of test results
* @param metrics The {@link Map} that contains metrics for the given test
*/
private void reportMetrics(ITestInvocationListener listener, Map<String, String> metrics)
throws IllegalArgumentException {
// Parse out only averages
Map<String, Map<String, String>> allMetrics = new HashMap<String, Map<String, String>>();
for (String key : metrics.keySet()) {
Matcher m = METRICS_PATTERN.matcher(metrics.get(key));
if (m.matches()) {
Map<String, String> parsedMetrics = new HashMap<String, String>();
parsedMetrics.put(String.format("%s_fgavg", key), m.group(1));
parsedMetrics.put(String.format("%s_bgavg", key), m.group(4));
String testLabel = TEST_TAG_MAP.get(key);
if (testLabel == null) {
testLabel = OTHER;
}
if (allMetrics.containsKey(testLabel)) {
allMetrics.get(testLabel).putAll(parsedMetrics);
} else {
allMetrics.put(testLabel, parsedMetrics);
}
}
else {
throw new IllegalArgumentException("Input text contains no metrics to parse");
}
}
for (String section : allMetrics.keySet()) {
Map<String, String> sectionMetrics = allMetrics.get(section);
if (sectionMetrics != null && !sectionMetrics.isEmpty()) {
CLog.d("About to report '%s' metrics: %s", section, sectionMetrics);
listener.testRunStarted(section, 0);
listener.testRunEnded(0, sectionMetrics);
}
}
}
IRunUtil getRunUtil() {
return RunUtil.getDefault();
}
}