/* * 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.graphics.tests; import com.android.ddmlib.IDevice; 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.BugreportCollector; import com.android.tradefed.result.ITestInvocationListener; import com.android.tradefed.result.InputStreamSource; import com.android.tradefed.result.LogDataType; import com.android.tradefed.result.SnapshotInputStreamSource; import com.android.tradefed.testtype.IDeviceTest; import com.android.tradefed.testtype.IRemoteTest; import com.android.tradefed.util.FileUtil; import com.android.tradefed.util.RunUtil; import com.android.tradefed.util.StreamUtil; import junit.framework.Assert; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Run UiPerformanceTest suite which measures the performance of * system operations and majors applications. */ public class UiPerformanceTest implements IDeviceTest, IRemoteTest { private ITestDevice mTestDevice = null; // Define instrumentation test package and runner. private static final String TEST_PACKAGE_NAME = "com.android.testing.uiautomation.platform.uiperformance"; // TODO: Add TEST_CLASS_NAME later when different tests requiring // different configurations. private static final String TEST_RUNNER_NAME = "com.android.testing.uiautomation.UiAutomationTestRunner"; private static String OUTPUT_FILE_NAME = "UiPerfTestsOutput.txt"; // output file private static String RAW_DATA_DIRECTORY = "UiPerformanceRawData"; // raw data directory private static final String TEST_CASE_PREFIX = "test"; private static final long START_TIMER = 2 * 60 * 1000; // 2 minutes private static final Pattern JANKINESS_PATTERN = Pattern.compile("^number of jankiness: (\\d+)"); private static final Pattern MEDIAN_FRAME_LATENCY_PATTERN = Pattern.compile("^median of frame latency: (\\d+)"); private static final Pattern FRAME_RATE_PATTERN = Pattern.compile("^average frame rate: (\\d+\\.\\d+)"); private static Pattern[] mPatterns = {JANKINESS_PATTERN, FRAME_RATE_PATTERN, MEDIAN_FRAME_LATENCY_PATTERN}; private static String[] ITEM_KEYS = {"number_jankiness", "frame_rate", "frame_latency"}; @Override public void setDevice(ITestDevice testDevice) { mTestDevice = testDevice; } @Override public ITestDevice getDevice() { return mTestDevice; } private void setupDevice() throws DeviceNotAvailableException { cleanOutputFiles(); String extStore = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); String rawFileDir = String.format("%s/%s", extStore, RAW_DATA_DIRECTORY); if (!mTestDevice.doesFileExist(rawFileDir)) { CLog.v(String.format("The raw directory %s doesn't exist.", RAW_DATA_DIRECTORY)); mTestDevice.executeShellCommand(String.format("mkdir \"%s\"", rawFileDir)); } else { // remove files mTestDevice.executeShellCommand(String.format("rm %s/*", rawFileDir)); CLog.v("remove files under the raw data directory"); } } /** * Run UiPerformanceTests and parsing results from test output. */ @Override public void run(ITestInvocationListener standardListener) throws DeviceNotAvailableException { Assert.assertNotNull(mTestDevice); setupDevice(); // start the test until device is fully booted and stable RunUtil.getDefault().sleep(START_TIMER); IRemoteAndroidTestRunner runner = new RemoteAndroidTestRunner( TEST_PACKAGE_NAME, TEST_RUNNER_NAME, mTestDevice.getIDevice()); // Add bugreport listener for failed test BugreportCollector bugListener = new BugreportCollector(standardListener, mTestDevice); bugListener.addPredicate(BugreportCollector.AFTER_FAILED_TESTCASES); bugListener.setDescriptiveName(this.getClass().getName()); mTestDevice.runInstrumentationTests(runner, bugListener); logOutputFile(bugListener); pullRawDataFile(bugListener); cleanOutputFiles(); } private void pullRawDataFile(ITestInvocationListener listener) throws DeviceNotAvailableException { try { String extStore = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); String rawFileDir = String.format("%s/%s", extStore, RAW_DATA_DIRECTORY); String rawFileList = mTestDevice.executeShellCommand(String.format("ls \"%s\"", rawFileDir)); String[] rawFileString = rawFileList.split("\r\n"); File resFile = null; InputStreamSource outputSource = null; for (int i = 0; i < rawFileString.length; i++) { CLog.v("file %d is: \"%s\"", i, rawFileString[i]); resFile = mTestDevice.pullFileFromExternal( String.format("%s/%s", RAW_DATA_DIRECTORY, rawFileString[i])); outputSource = new SnapshotInputStreamSource(new FileInputStream(resFile)); listener.testLog(rawFileString[i], LogDataType.TEXT, outputSource); } } catch (IOException e) { CLog.e("IOException while reading raw data files: " + e.toString()); } } // Parse the output file private void logOutputFile(ITestInvocationListener listener) throws DeviceNotAvailableException { // catch a bugreport after the test InputStreamSource bugreport = mTestDevice.getBugreport(); listener.testLog("bugreport", LogDataType.TEXT, bugreport); bugreport.cancel(); File resFile = null; InputStreamSource outputSource = null; Map<String, String> runMetrics = new HashMap<String, String>(); BufferedReader br = null; try { resFile = mTestDevice.pullFileFromExternal(OUTPUT_FILE_NAME); if (resFile == null) { CLog.v("File %s doesn't exist or pulling the file failed"); return; } CLog.d("output file: %s", resFile.getPath()); // Save a copy of the output file CLog.d("Sending %d byte file %s into the logosphere!", resFile.length(), resFile); outputSource = new SnapshotInputStreamSource(new FileInputStream(resFile)); listener.testLog(OUTPUT_FILE_NAME, LogDataType.TEXT, outputSource); // Parse the results file br = new BufferedReader(new FileReader(resFile)); String line = null; String unitKey = null; int size = mPatterns.length; while ((line = br.readLine()) != null) { if (line.startsWith(TEST_CASE_PREFIX)) { // report the previous test case results if (unitKey != null) { reportMetrics(unitKey, listener, runMetrics); } runMetrics.clear(); // processing the next test case unitKey = line.trim(); continue; } else { for (int i = 0; i < size; i++) { Matcher match = mPatterns[i].matcher(line); if (match.matches()) { String value = match.group(1); runMetrics.put(ITEM_KEYS[i], value); break; } } } } reportMetrics(unitKey, listener, runMetrics); } catch (IOException e) { CLog.e("IOException while reading outputfile %s", OUTPUT_FILE_NAME); } finally { FileUtil.deleteFile(resFile); StreamUtil.cancel(outputSource); StreamUtil.close(br); } } // Report run metrics by creating an empty test run to stick them in private void reportMetrics(String metricsName, ITestInvocationListener listener, Map<String, String> metrics) { // Create an empty testRun to report the parsed runMetrics CLog.d("About to report metrics to %s: %s", metricsName, metrics); listener.testRunStarted(metricsName, 0); listener.testRunEnded(0, metrics); } // clean up output file private void cleanOutputFiles() throws DeviceNotAvailableException { String extStore = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); mTestDevice.executeShellCommand(String.format("rm %s/%s", extStore, OUTPUT_FILE_NAME)); } }