/* * Copyright (C) 2011 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.wireless.tests; import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner; import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; import com.android.tradefed.config.Option; 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.RegexTrie; 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.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Run telephony stability test. The test stresses the stability of telephony by * putting device into sleep mode and wake up, voice connection and data connection * are verified after device wakeup. */ public class TelephonyStabilityTest implements IRemoteTest, IDeviceTest { private static final String TEST_NAME = "TelephonyTest"; private static final String OUTPUT_FILE = "/data/data/com.android.phone/files/phoneResults.txt"; // Define report RU private static final String METRICS_NAME = "telephony_stability"; // Define instrumentation test package and runner. private static final String TEST_PACKAGE_NAME = "com.android.phonetests"; private static final String TEST_RUNNER_NAME = ".PhoneInstrumentationStressTestRunner"; private static final String TEST_CLASS_NAME = "com.android.phonetests.stress.telephony.TelephonyStress"; private static final String TEST_METHOD = "testTelephonyStability"; private static final int TEST_TIMER = 9 * 60 * 60 * 1000; // 9 hours private static final Pattern ITERATION_PATTERN = Pattern.compile("^iteration (\\d+) out of (\\d+)"); private static final String VOICE_REGISTRATION_KEY = "voice_registration"; private static final String VOICE_CONNECTION_KEY = "voice_call"; private static final String DATA_REGISTRATION_KEY = "data_registration"; private static final String DATA_CONNECTION_KEY = "data_connection"; private ITestDevice mTestDevice = null; private RegexTrie<String> mPatternMap = null; Map<String, String> mRunMetrics = new HashMap<String, String>(); private int mResIndex = 0; private RadioHelper mRadioHelper; @Option(name="call-duration", description="The time of a call to be held in the test (in seconds)") private String mCallDuration = "60"; @Option(name="phone-number", description="The phone number used for outgoing call test") private String mPhoneNumber = null; @Option(name="iteration", description="The number of calls to make during the test") private int mIteration = 100; @Option(name="idletime", description="The time to allow device staty in suspend mode (in seconds)") private int mIdleTime = 120; @Option(name="screen-time-out", description="Set screen timer (in minutes)") private int mScreenTimer = 30; /** * Configure screen timeout property * @throws DeviceNotAvailableException */ private void configDevice() throws DeviceNotAvailableException { int timeOut = mScreenTimer * 60 * 1000; String command = ("sqlite3 /data/data/com.android.providers.settings/databases/settings.db " + "\"UPDATE system SET value=\'" + timeOut + "\' WHERE name=\'screen_off_timeout\';\""); CLog.d("Command to set screen timeout value to %d minutes: %s", mScreenTimer, command); mTestDevice.executeShellCommand(command); // Set device screen_off_timeout as svc power can be set to false in the Wi-Fi test mTestDevice.executeShellCommand("svc power stayon false"); // reboot to allow the setting to take effect, post setup will be taken care by the reboot mTestDevice.reboot(); } private void setupTest() { mPatternMap = new RegexTrie<String>(); mPatternMap.put(VOICE_REGISTRATION_KEY, "^Voice registration: (\\d+)"); mPatternMap.put(VOICE_CONNECTION_KEY, "^Voice connection: (\\d+)"); mPatternMap.put(DATA_REGISTRATION_KEY, "^Data registration: (\\d+)"); mPatternMap.put(DATA_CONNECTION_KEY, "^Data connection: (\\d+)"); String value = "0"; mRunMetrics.put(VOICE_REGISTRATION_KEY, value); mRunMetrics.put(VOICE_CONNECTION_KEY, value); mRunMetrics.put(DATA_REGISTRATION_KEY, value); mRunMetrics.put(DATA_CONNECTION_KEY, value); } /** * Run the telephony stability test and collect results */ @Override public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.d("input options: mCallDuration(%s), mPhoneNumber(%s), mIteration(%d), " + "mIdleTime(%d), mScreenTimer(%d)", mCallDuration, mPhoneNumber, mIteration, mIdleTime, mScreenTimer); Assert.assertNotNull(mTestDevice); Assert.assertNotNull(mPhoneNumber); configDevice(); setupTest(); mRadioHelper = new RadioHelper(mTestDevice); if (!mRadioHelper.radioActivation() || !mRadioHelper.waitForDataSetup()) { mRadioHelper.getBugreport(listener); return; } IRemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(TEST_PACKAGE_NAME, TEST_RUNNER_NAME, mTestDevice.getIDevice()); runner.setClassName(TEST_CLASS_NAME); runner.setMethodName(TEST_CLASS_NAME, TEST_METHOD); runner.addInstrumentationArg("callduration", mCallDuration); runner.addInstrumentationArg("phonenumber", mPhoneNumber); runner.addInstrumentationArg("idletime", Integer.toString(mIdleTime)); runner.setMaxtimeToOutputResponse(TEST_TIMER); // Add bugreport listener for failed test BugreportCollector bugListener = new BugreportCollector(listener, mTestDevice); bugListener.addPredicate(BugreportCollector.AFTER_FAILED_TESTCASES); bugListener.setDescriptiveName(TEST_NAME); // Device may reboot during the test, to capture a bugreport after that, // wait for 30 seconds for device to be online, otherwise, bugreport will be empty bugListener.setDeviceWaitTime(30); int remainingIteration = mIteration; while (remainingIteration > 0) { runner.addInstrumentationArg("iteration", String.valueOf(remainingIteration)); mTestDevice.runInstrumentationTests(runner, bugListener); int testRun = logOutputFile(bugListener); remainingIteration -= testRun; CLog.d("remainingIteration: %d", remainingIteration); cleanOutputFiles(); } reportMetrics(bugListener); } /** * Collect results from the previous run * @param listener */ private int logOutputFile(ITestInvocationListener listener) throws DeviceNotAvailableException { File resFile = null; InputStreamSource outputSource = null; resFile = mTestDevice.pullFile(OUTPUT_FILE); int testRun = 0; BufferedReader br = null; try { if (resFile == null) { // If the result file is empty, either system crash or there are other fails // (e.g. failed to connect to mobile network after bootup), count as a failed // iteration return 1; } // 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(String.format("result_%d", mResIndex++), LogDataType.TEXT, outputSource); br = new BufferedReader(new FileReader(resFile)); String line = null; while ((line = br.readLine()) != null) { Matcher m = ITERATION_PATTERN.matcher(line); if (m.matches()) { testRun = Integer.parseInt(m.group(1)); CLog.d("test run: %d", testRun); } else { List<List<String>> capture = new ArrayList<List<String>>(1); String key = mPatternMap.retrieve(capture, line); if (key != null) { // retrive from the metrics, add the new value and put it back int value = Integer.parseInt(mRunMetrics.get(key)); value += Integer.parseInt(capture.get(0).get(0)); mRunMetrics.put(key, Integer.toString(value)); } } } } catch (IOException e) { CLog.e("IOException while reading outputfile %s", resFile.getAbsolutePath()); } finally { FileUtil.deleteFile(resFile); StreamUtil.cancel(outputSource); StreamUtil.close(br); } return (testRun + 1); } /** * Report run metrics by creating an empty test run to stick them in * <p /> * Exposed for unit testing */ private void reportMetrics(ITestInvocationListener listener) { // Create an empty testRun to report the parsed runMetrics CLog.d("About to report metrics to %s: %s", METRICS_NAME, mRunMetrics); listener.testRunStarted(METRICS_NAME, 0); listener.testRunEnded(0, mRunMetrics); } /** * Clean up output files from the last test run */ private void cleanOutputFiles() throws DeviceNotAvailableException { CLog.d("Remove output file: %s", OUTPUT_FILE); mTestDevice.executeShellCommand(String.format("rm %s", OUTPUT_FILE)); } @Override public void setDevice(ITestDevice testDevice) { mTestDevice = testDevice; } @Override public ITestDevice getDevice() { return mTestDevice; } }