Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.wireless.tests;
     17 
     18 import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner;
     19 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
     20 import com.android.tradefed.config.Option;
     21 import com.android.tradefed.device.DeviceNotAvailableException;
     22 import com.android.tradefed.device.ITestDevice;
     23 import com.android.tradefed.log.LogUtil.CLog;
     24 import com.android.tradefed.result.ITestInvocationListener;
     25 import com.android.tradefed.result.InputStreamSource;
     26 import com.android.tradefed.result.LogDataType;
     27 import com.android.tradefed.testtype.IDeviceTest;
     28 import com.android.tradefed.testtype.IRemoteTest;
     29 import com.android.tradefed.util.FileUtil;
     30 import com.android.tradefed.util.RegexTrie;
     31 import com.android.tradefed.util.StreamUtil;
     32 
     33 import org.junit.Assert;
     34 
     35 import java.io.BufferedReader;
     36 import java.io.File;
     37 import java.io.FileReader;
     38 import java.io.IOException;
     39 import java.util.HashMap;
     40 import java.util.Map;
     41 import java.util.concurrent.TimeUnit;
     42 import java.util.regex.Matcher;
     43 import java.util.regex.Pattern;
     44 
     45 /**
     46  * Run telephony stability test. The test stresses the stability of telephony by
     47  * putting device into sleep mode and wake up, voice connection and data connection
     48  * are verified after device wakeup.
     49  */
     50 public class TelephonyStabilityTest implements IRemoteTest, IDeviceTest {
     51     private static final int TEST_TIMEOUT = 9 * 60 * 60 * 1000; // 9 hours
     52 
     53     // Number of iterations to skip taking bugreports for.  This is to avoid taking 1000 bugreports
     54     // if there are 1000 failures
     55     private static final int BUGREPORT_SKIP_ITERATIONS = 10;
     56 
     57     private static final String METRICS_NAME = "telephony_stability";
     58     private static final String VOICE_REGISTRATION_KEY = "voice_registration";
     59     private static final String VOICE_CONNECTION_KEY = "voice_call";
     60     private static final String DATA_REGISTRATION_KEY = "data_registration";
     61     private static final String DATA_CONNECTION_KEY = "data_connection";
     62     private static final String TEST_FAILURE_KEY = "test_failure";
     63 
     64     private static final String OUTPUT_FILE = "output.txt";
     65     // Output file in format:
     66     // iteration voice_registration voice_connection data_registration data_connection
     67     private static final Pattern OUTPUT_LINE_REGEX = Pattern.compile(
     68             "(\\d+) (\\d+) (\\d+) (\\d+) (\\d+)");
     69     private static final RegexTrie<String> OUTPUT_KEY_PATTERNS = new RegexTrie<String>();
     70     static {
     71         OUTPUT_KEY_PATTERNS.put(VOICE_REGISTRATION_KEY, "^Voice registration: (\\d+)");
     72         OUTPUT_KEY_PATTERNS.put(VOICE_CONNECTION_KEY, "^Voice connection: (\\d+)");
     73         OUTPUT_KEY_PATTERNS.put(DATA_REGISTRATION_KEY, "^Data registration: (\\d+)");
     74         OUTPUT_KEY_PATTERNS.put(DATA_CONNECTION_KEY, "^Data connection: (\\d+)");
     75     }
     76 
     77     // Define instrumentation test package and runner.
     78     private static final String TEST_PACKAGE_NAME = "com.android.phonetests";
     79     private static final String TEST_RUNNER_NAME = ".PhoneInstrumentationStressTestRunner";
     80     private static final String TEST_CLASS_NAME =
     81             "com.android.phonetests.stress.telephony.TelephonyStress2";
     82     private static final String TEST_METHOD = "testStability";
     83 
     84     private static final String SETTINGS_DB = "/data/data/com.android.providers.settings/databases/"
     85             + "settings.db";
     86     private static final String LOCKSCREEN_DB = "/data/system/locksettings.db";
     87 
     88     @Option(name="phone-number",
     89             description="The phone number used for outgoing call test")
     90     private String mPhoneNumber = null;
     91 
     92     @Option(name="iterations",
     93             description="The number of calls to make during the test")
     94     private int mIterations = 100;
     95 
     96     @Option(name="call-duration",
     97             description="The time of a call to be held in the test (in seconds)")
     98     private long mCallDurationSec = 60;
     99 
    100     @Option(name="suspend-duration",
    101             description="The time to allow device staty in suspend mode (in seconds)")
    102     private long mSuspendDurationSec = 120;
    103 
    104     @Option(name="screen-timeout",
    105             description="Set screen timer (in minutes)")
    106     private long mScreenTimeoutMin = 30;
    107 
    108     private ITestDevice mTestDevice = null;
    109 
    110     /**
    111      * Run the telephony stability test  and collect results
    112      */
    113     @Override
    114     public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
    115         Assert.assertNotNull(mTestDevice);
    116         Assert.assertNotNull(mPhoneNumber);
    117 
    118         setScreenTimeout();
    119 
    120         final RadioHelper radioHelper = new RadioHelper(mTestDevice);
    121         Assert.assertTrue("Radio activation failed", radioHelper.radioActivation());
    122         Assert.assertTrue("Data setup failed", radioHelper.waitForDataSetup());
    123 
    124         IRemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(TEST_PACKAGE_NAME,
    125                 TEST_RUNNER_NAME, mTestDevice.getIDevice());
    126         runner.setClassName(TEST_CLASS_NAME);
    127         runner.setMethodName(TEST_CLASS_NAME, TEST_METHOD);
    128 
    129         runner.addInstrumentationArg("phone-number", mPhoneNumber);
    130         runner.addInstrumentationArg("iterations", Integer.toString(mIterations));
    131         runner.addInstrumentationArg("call-duration", Long.toString(mCallDurationSec));
    132         runner.addInstrumentationArg("suspend-duration", Long.toString(mSuspendDurationSec));
    133         runner.setMaxTimeToOutputResponse(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
    134 
    135         Map<String, Integer> metrics = new HashMap<String, Integer>(4);
    136         metrics.put(VOICE_REGISTRATION_KEY, 0);
    137         metrics.put(VOICE_CONNECTION_KEY, 0);
    138         metrics.put(DATA_REGISTRATION_KEY, 0);
    139         metrics.put(DATA_CONNECTION_KEY, 0);
    140         metrics.put(TEST_FAILURE_KEY, 0);
    141 
    142         int currentIteration = 0;
    143         Integer lastBugreportIteration = null;
    144         while (currentIteration < mIterations) {
    145             runner.addInstrumentationArg("current-iteration", Integer.toString(currentIteration));
    146             mTestDevice.runInstrumentationTests(runner, listener);
    147             mTestDevice.waitForDeviceOnline(30 * 1000);
    148             currentIteration = processOutputFile(currentIteration, metrics) + 1;
    149 
    150             // Take a bugreport if at last iteration, if we havent taken a bugreport, or if the
    151             // bugreport was taken more than BUGREPORT_SKIP_ITERATIONS ago.
    152             boolean shouldTakeReport = (currentIteration >= mIterations ||
    153                     lastBugreportIteration == null ||
    154                     (currentIteration - lastBugreportIteration) > BUGREPORT_SKIP_ITERATIONS);
    155 
    156             if (shouldTakeReport) {
    157                 lastBugreportIteration = currentIteration - 1;
    158                 InputStreamSource bugreport = mTestDevice.getBugreport();
    159                 try {
    160                     listener.testLog(String.format("bugreport_%04d", lastBugreportIteration),
    161                             LogDataType.BUGREPORT, bugreport);
    162                 } finally {
    163                     bugreport.cancel();
    164                 }
    165             }
    166 
    167             InputStreamSource screenshot = mTestDevice.getScreenshot();
    168             try {
    169                 listener.testLog(String.format("screenshot_%04d", lastBugreportIteration),
    170                         LogDataType.PNG, screenshot);
    171             } finally {
    172                 StreamUtil.cancel(screenshot);
    173             }
    174         }
    175         reportMetrics(listener, metrics);
    176     }
    177 
    178     /**
    179      * Configure screen timeout property and lockscreen
    180      */
    181     private void setScreenTimeout() throws DeviceNotAvailableException {
    182         String command = String.format("sqlite3 %s \"UPDATE system SET value=\'%s\' "
    183                 + "WHERE name=\'screen_off_timeout\';\"", SETTINGS_DB,
    184                 mScreenTimeoutMin * 60 * 1000);
    185         mTestDevice.executeShellCommand(command);
    186 
    187         command = String.format("sqlite3 %s \"UPDATE locksettings SET value=\'1\' "
    188                 + "WHERE name=\'lockscreen.disabled\'\"", LOCKSCREEN_DB);
    189         mTestDevice.executeShellCommand(command);
    190 
    191         command = String.format("sqlite3 %s \"UPDATE locksettings SET value=\'0\' "
    192                 + "WHERE name=\'lockscreen.password_type\'\"", LOCKSCREEN_DB);
    193         mTestDevice.executeShellCommand(command);
    194 
    195         command = String.format("sqlite3 %s \"UPDATE locksettings SET value=\'0\' "
    196                 + "WHERE name=\'lockscreen.password_type_alternate\'\"", LOCKSCREEN_DB);
    197         mTestDevice.executeShellCommand(command);
    198 
    199         // Set device screen_off_timeout as svc power can be set to false in the Wi-Fi test
    200         mTestDevice.executeShellCommand("svc power stayon false");
    201 
    202         // reboot to allow the setting to take effect, post setup will be taken care by the reboot
    203         mTestDevice.reboot();
    204     }
    205 
    206     /**
    207      * Process the output file, add the failure reason to the file, and return the current
    208      * iteration that the call failed on.
    209      */
    210     private int processOutputFile(int currentIteration, Map<String, Integer> metrics)
    211             throws DeviceNotAvailableException {
    212         final File resFile = mTestDevice.pullFileFromExternal(OUTPUT_FILE);
    213         BufferedReader reader = null;
    214         try {
    215             if (resFile == null) {
    216                 CLog.w("Output file did not exist");
    217                 metrics.put(TEST_FAILURE_KEY, metrics.get(TEST_FAILURE_KEY) + 1);
    218                 return currentIteration;
    219             }
    220             reader = new BufferedReader(new FileReader(resFile));
    221             String line = getLastLine(reader);
    222 
    223             if (line == null) {
    224                 CLog.w("Output file was emtpy");
    225                 metrics.put(TEST_FAILURE_KEY, metrics.get(TEST_FAILURE_KEY) + 1);
    226                 return currentIteration;
    227             }
    228 
    229             Matcher m = OUTPUT_LINE_REGEX.matcher(line);
    230             if (!m.matches()) {
    231                 CLog.w("Output did not match the expected pattern. Line was \"%s\"", line);
    232                 metrics.put(TEST_FAILURE_KEY, metrics.get(TEST_FAILURE_KEY) + 1);
    233                 return currentIteration;
    234             }
    235 
    236             CLog.i("Parsed line was \"%s\"", line);
    237 
    238             final int recordedIteration = Integer.parseInt(m.group(1));
    239             metrics.put(VOICE_REGISTRATION_KEY,
    240                     metrics.get(VOICE_REGISTRATION_KEY) + Integer.parseInt(m.group(2)));
    241             metrics.put(VOICE_CONNECTION_KEY,
    242                     metrics.get(VOICE_CONNECTION_KEY) + Integer.parseInt(m.group(3)));
    243             metrics.put(DATA_REGISTRATION_KEY,
    244                     metrics.get(DATA_REGISTRATION_KEY) + Integer.parseInt(m.group(4)));
    245             metrics.put(DATA_CONNECTION_KEY,
    246                     metrics.get(DATA_CONNECTION_KEY) + Integer.parseInt(m.group(5)));
    247 
    248             return Math.max(recordedIteration, currentIteration);
    249         } catch (IOException e) {
    250             CLog.e("IOException while reading outputfile %s", resFile.getAbsolutePath());
    251             return currentIteration;
    252         } finally {
    253             FileUtil.deleteFile(resFile);
    254             StreamUtil.close(reader);
    255             mTestDevice.executeShellCommand(String.format("rm ${EXTERNAL_STORAGE}/%s",
    256                     OUTPUT_FILE));
    257         }
    258     }
    259 
    260     /**
    261      * Get the last line from a buffered reader.
    262      */
    263     private String getLastLine(BufferedReader reader) throws IOException {
    264         String lastLine = null;
    265         String currentLine;
    266         while ((currentLine = reader.readLine()) != null) {
    267             lastLine = currentLine;
    268         }
    269         return lastLine;
    270     }
    271 
    272     /**
    273      * Report run metrics by creating an empty test run to stick them in
    274      */
    275     private void reportMetrics(ITestInvocationListener listener, Map<String, Integer> metrics) {
    276         Map<String, String> reportedMetrics = new HashMap<String, String>();
    277         for (Map.Entry<String, Integer> entry : metrics.entrySet()) {
    278             final Integer keyFailures = entry.getValue();
    279             reportedMetrics.put(entry.getKey(), keyFailures.toString());
    280         }
    281 
    282         // Create an empty testRun to report the parsed runMetrics
    283         CLog.d("About to report metrics to %s: %s", METRICS_NAME, reportedMetrics);
    284         listener.testRunStarted(METRICS_NAME, 0);
    285         listener.testRunEnded(0, reportedMetrics);
    286     }
    287 
    288     /**
    289      * {@inheritDoc}
    290      */
    291     @Override
    292     public void setDevice(ITestDevice testDevice) {
    293         mTestDevice = testDevice;
    294     }
    295 
    296     /**
    297      * {@inheritDoc}
    298      */
    299     @Override
    300     public ITestDevice getDevice() {
    301         return mTestDevice;
    302     }
    303 }
    304