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.IDevice;
     19 import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner;
     20 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
     21 import com.android.tradefed.config.Option;
     22 import com.android.tradefed.device.DeviceNotAvailableException;
     23 import com.android.tradefed.device.ITestDevice;
     24 import com.android.tradefed.log.LogUtil.CLog;
     25 import com.android.tradefed.result.BugreportCollector;
     26 import com.android.tradefed.result.FileInputStreamSource;
     27 import com.android.tradefed.result.ITestInvocationListener;
     28 import com.android.tradefed.result.InputStreamSource;
     29 import com.android.tradefed.result.LogDataType;
     30 import com.android.tradefed.testtype.IDeviceTest;
     31 import com.android.tradefed.testtype.IRemoteTest;
     32 import com.android.tradefed.util.FileUtil;
     33 import com.android.tradefed.util.RegexTrie;
     34 import com.android.tradefed.util.RunUtil;
     35 import com.android.tradefed.util.StreamUtil;
     36 
     37 import org.junit.Assert;
     38 
     39 import java.io.BufferedReader;
     40 import java.io.File;
     41 import java.io.FileReader;
     42 import java.io.IOException;
     43 import java.util.ArrayList;
     44 import java.util.HashMap;
     45 import java.util.List;
     46 import java.util.Map;
     47 import java.util.concurrent.TimeUnit;
     48 import java.util.regex.Matcher;
     49 import java.util.regex.Pattern;
     50 
     51 /**
     52  * Run the WiFi stress tests. This test stresses WiFi soft ap, WiFi scanning
     53  * and WiFi reconnection in which device switches between cellular and WiFi connection.
     54  */
     55 public class WifiStressTest implements IRemoteTest, IDeviceTest {
     56     private ITestDevice mTestDevice = null;
     57     private static final long START_TIMER = 5 * 60 * 1000; //5 minutes
     58     // Define instrumentation test package and runner.
     59     private static final String TEST_PACKAGE_NAME = "com.android.connectivitymanagertest";
     60     private static final String TEST_RUNNER_NAME = ".ConnectivityManagerStressTestRunner";
     61 
     62     private static final Pattern ITERATION_PATTERN =
     63         Pattern.compile("^iteration (\\d+) out of (\\d+)");
     64     private static final int AP_TEST_TIMER = 3 * 60 * 60 * 1000; // 3 hours
     65     private static final int SCAN_TEST_TIMER = 30 * 60 * 1000; // 30 minutes
     66     private static final int RECONNECT_TEST_TIMER = 12 * 60 * 60 * 1000; // 12 hours
     67 
     68     private String mOutputFile = "WifiStressTestOutput.txt";
     69 
     70     /**
     71      * Stores the test cases that we should consider running.
     72      * <p/>
     73      * This currently consists of "ap", "scanning", and "reconnection" tests.
     74      */
     75     private List<TestInfo> mTestList = null;
     76 
     77     private static class TestInfo {
     78         public String mTestName = null;
     79         public String mTestClass = null;
     80         public String mTestMethod = null;
     81         public String mTestMetricsName = null;
     82         public int mTestTimer;
     83         public RegexTrie<String> mPatternMap = null;
     84 
     85         @Override
     86         public String toString() {
     87             return String.format("TestInfo: mTestName(%s), mTestClass(%s), mTestMethod(%s)," +
     88                     " mTestMetricsName(%s), mPatternMap(%s), mTestTimer(%d)", mTestName,
     89                     mTestClass, mTestMethod, mTestMetricsName, mPatternMap.toString(), mTestTimer);
     90         }
     91     }
     92 
     93     @Option(name="ap-iteration",
     94             description="The number of iterations to run soft ap stress test")
     95     private String mApIteration = "100";
     96 
     97     @Option(name="idle-time",
     98         description="The device idle time after screen off")
     99     private String mIdleTime = "30"; // 30 seconds
    100 
    101     @Option(name="reconnect-iteration",
    102             description="The number of iterations to run WiFi reconnection stress test")
    103     private String mReconnectionIteration = "100";
    104 
    105     @Option(name="reconnect-password",
    106             description="The password for the above ssid in WiFi reconnection stress test")
    107     private String mReconnectionPassword = "androidwifi";
    108 
    109     @Option(name="reconnect-ssid",
    110         description="The ssid for WiFi recoonection stress test")
    111     private String mReconnectionSsid = "securenetdhcp";
    112 
    113     @Option(name="reconnection-test",
    114         description="Option to run the wifi reconnection stress test")
    115     private boolean mReconnectionTestFlag = true;
    116 
    117     @Option(name="scan-iteration",
    118         description="The number of iterations to run WiFi scanning test")
    119     private String mScanIteration = "100";
    120 
    121     @Option(name="scan-test",
    122             description="Option to run the scan stress test")
    123     private boolean mScanTestFlag = true;
    124 
    125     @Option(name="skip-set-device-screen-timeout",
    126             description="Option to skip screen timeout configuration")
    127     private boolean mSkipSetDeviceScreenTimeout = false;
    128 
    129     @Option(name="tether-test",
    130             description="Option to run the tethering stress test")
    131     private boolean mTetherTestFlag = true;
    132 
    133     @Option(name="wifi-only")
    134     private boolean mWifiOnly = false;
    135 
    136     private void setupTests() {
    137         if (mTestList != null) {
    138             return;
    139         }
    140         mTestList = new ArrayList<>(3);
    141 
    142         // Add WiFi AP stress test
    143         TestInfo t = new TestInfo();
    144         t.mTestName = "WifiAPStress";
    145         t.mTestClass = "com.android.connectivitymanagertest.stress.WifiApStress";
    146         t.mTestMethod = "testWifiHotSpot";
    147         t.mTestMetricsName = "wifi_stress";
    148         t.mTestTimer = AP_TEST_TIMER;
    149         t.mPatternMap = new RegexTrie<>();
    150         t.mPatternMap.put("wifi_ap_stress", ITERATION_PATTERN);
    151         if (mTetherTestFlag) {
    152             mTestList.add(t);
    153         }
    154 
    155         // Add WiFi scanning test
    156         t = new TestInfo();
    157         t.mTestName = "WifiScanning";
    158         t.mTestClass = "com.android.connectivitymanagertest.stress.WifiStressTest";
    159         t.mTestMethod = "testWifiScanning";
    160         t.mTestMetricsName = "wifi_scan_performance";
    161         t.mTestTimer = SCAN_TEST_TIMER;
    162         t.mPatternMap = new RegexTrie<>();
    163         t.mPatternMap.put("avg_scan_time", "^average scanning time is (\\d+)");
    164         t.mPatternMap.put("scan_quality","ssid appear (\\d+) out of (\\d+) scan iterations");
    165         if (mScanTestFlag) {
    166             mTestList.add(t);
    167         }
    168 
    169         // Add WiFi reconnection test
    170         t = new TestInfo();
    171         t.mTestName = "WifiReconnectionStress";
    172         t.mTestClass = "com.android.connectivitymanagertest.stress.WifiStressTest";
    173         t.mTestMethod = "testWifiReconnectionAfterSleep";
    174         t.mTestMetricsName = "wifi_stress";
    175         t.mTestTimer = RECONNECT_TEST_TIMER;
    176         t.mPatternMap = new RegexTrie<>();
    177         t.mPatternMap.put("wifi_reconnection_stress", ITERATION_PATTERN);
    178         if (mReconnectionTestFlag) {
    179             mTestList.add(t);
    180         }
    181     }
    182 
    183     /**
    184      * Configure screen timeout property
    185      * @throws DeviceNotAvailableException
    186      */
    187     private void setDeviceScreenTimeout() throws DeviceNotAvailableException {
    188         // Set device screen_off_timeout as svc power can be set to false in the Wi-Fi test
    189         String command = ("sqlite3 /data/data/com.android.providers.settings/databases/settings.db "
    190                 + "\"UPDATE system SET value=\'600000\' WHERE name=\'screen_off_timeout\';\"");
    191         CLog.d("Command to set screen timeout value to 10 minutes: %s", command);
    192         mTestDevice.executeShellCommand(command);
    193 
    194         // reboot to allow the setting to take effect, post setup will be taken care by the reboot
    195         mTestDevice.reboot();
    196     }
    197 
    198     /**
    199      * Enable/disable screen never timeout property
    200      * @param on
    201      * @throws DeviceNotAvailableException
    202      */
    203     private void setScreenProperty(boolean on) throws DeviceNotAvailableException {
    204         CLog.d("set svc power stay on " + on);
    205         mTestDevice.executeShellCommand("svc power stayon " + on);
    206     }
    207 
    208     @Override
    209     public void setDevice(ITestDevice testDevice) {
    210         mTestDevice = testDevice;
    211     }
    212 
    213     @Override
    214     public ITestDevice getDevice() {
    215         return mTestDevice;
    216     }
    217 
    218     /**
    219      * Run the Wi-Fi stress test
    220      * Collect results and post results to dashboard
    221      */
    222     @Override
    223     public void run(ITestInvocationListener standardListener)
    224             throws DeviceNotAvailableException {
    225         Assert.assertNotNull(mTestDevice);
    226         setupTests();
    227         if (!mSkipSetDeviceScreenTimeout) {
    228             setDeviceScreenTimeout();
    229         }
    230         RunUtil.getDefault().sleep(START_TIMER);
    231 
    232         if (!mWifiOnly) {
    233             final RadioHelper radioHelper = new RadioHelper(mTestDevice);
    234             Assert.assertTrue("Radio activation failed", radioHelper.radioActivation());
    235             Assert.assertTrue("Data setup failed", radioHelper.waitForDataSetup());
    236         }
    237 
    238         IRemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(
    239                 TEST_PACKAGE_NAME, TEST_RUNNER_NAME, mTestDevice.getIDevice());
    240         runner.addInstrumentationArg("softap_iterations", mApIteration);
    241         runner.addInstrumentationArg("scan_iterations", mScanIteration);
    242         runner.addInstrumentationArg("reconnect_iterations", mReconnectionIteration);
    243         runner.addInstrumentationArg("reconnect_ssid", mReconnectionSsid);
    244         runner.addInstrumentationArg("reconnect_password", mReconnectionPassword);
    245         runner.addInstrumentationArg("sleep_time", mIdleTime);
    246         if (mWifiOnly) {
    247             runner.addInstrumentationArg("wifi-only", String.valueOf(mWifiOnly));
    248         }
    249 
    250         // Add bugreport listener for failed test
    251         BugreportCollector bugListener = new
    252             BugreportCollector(standardListener, mTestDevice);
    253         bugListener.addPredicate(BugreportCollector.AFTER_FAILED_TESTCASES);
    254         // Device may reboot during the test, to capture a bugreport after that,
    255         // wait for 30 seconds for device to be online, otherwise, bugreport will be empty
    256         bugListener.setDeviceWaitTime(30);
    257 
    258         for (TestInfo testCase : mTestList) {
    259             // for Wi-Fi reconnection test,
    260             if ("WifiReconnectionStress".equals(testCase.mTestName)) {
    261                 setScreenProperty(false);
    262             } else {
    263                 setScreenProperty(true);
    264             }
    265             CLog.d("TestInfo: " + testCase.toString());
    266             runner.setClassName(testCase.mTestClass);
    267             runner.setMethodName(testCase.mTestClass, testCase.mTestMethod);
    268             runner.setMaxTimeToOutputResponse(testCase.mTestTimer, TimeUnit.MILLISECONDS);
    269             bugListener.setDescriptiveName(testCase.mTestName);
    270             mTestDevice.runInstrumentationTests(runner, bugListener);
    271             logOutputFile(testCase, bugListener);
    272             cleanOutputFiles();
    273         }
    274     }
    275 
    276     /**
    277      * Collect test results, report test results to dash board.
    278      *
    279      * @param test
    280      * @param listener
    281      */
    282     private void logOutputFile(TestInfo test, ITestInvocationListener listener)
    283         throws DeviceNotAvailableException {
    284         File resFile = null;
    285         InputStreamSource outputSource = null;
    286 
    287         try {
    288             resFile = mTestDevice.pullFileFromExternal(mOutputFile);
    289             if (resFile != null) {
    290                 // Save a copy of the output file
    291                 CLog.d("Sending %d byte file %s into the logosphere!",
    292                         resFile.length(), resFile);
    293                 outputSource = new FileInputStreamSource(resFile);
    294                 listener.testLog(String.format("result-%s.txt", test.mTestName), LogDataType.TEXT,
    295                         outputSource);
    296 
    297                 // Parse the results file and post results to test listener
    298                 parseOutputFile(test, resFile, listener);
    299             }
    300         } finally {
    301             FileUtil.deleteFile(resFile);
    302             StreamUtil.cancel(outputSource);
    303         }
    304     }
    305 
    306     private void parseOutputFile(TestInfo test, File dataFile,
    307             ITestInvocationListener listener) {
    308         Map<String, String> runMetrics = new HashMap<>();
    309         Map<String, String> runScanMetrics = null;
    310         boolean isScanningTest = "WifiScanning".equals(test.mTestName);
    311         Integer iteration = null;
    312         BufferedReader br = null;
    313         try {
    314             br = new BufferedReader(new FileReader(dataFile));
    315             String line = null;
    316             while ((line = br.readLine()) != null) {
    317                 List<List<String>> capture = new ArrayList<>(1);
    318                 String key = test.mPatternMap.retrieve(capture, line);
    319                 if (key != null) {
    320                     CLog.d("In output file of test case %s: retrieve key: %s, " +
    321                             "catpure: %s", test.mTestName, key, capture.toString());
    322                     //Save results in the metrics
    323                     if ("scan_quality".equals(key)) {
    324                         // For scanning test, calculate the scan quality
    325                         int count = Integer.parseInt(capture.get(0).get(0));
    326                         int total = Integer.parseInt(capture.get(0).get(1));
    327                         int quality = 0;
    328                         if (total != 0) {
    329                             quality = (100 * count) / total;
    330                         }
    331                         runMetrics.put(key, Integer.toString(quality));
    332                     } else {
    333                         runMetrics.put(key, capture.get(0).get(0));
    334                     }
    335                 } else {
    336                     // For scanning test, iterations will also be counted.
    337                     if (isScanningTest) {
    338                         Matcher m = ITERATION_PATTERN.matcher(line);
    339                         if (m.matches()) {
    340                             iteration = Integer.parseInt(m.group(1));
    341                         }
    342                     }
    343                 }
    344             }
    345             if (isScanningTest) {
    346                 runScanMetrics = new HashMap<>(1);
    347                 if (iteration == null) {
    348                     // no matching is found
    349                     CLog.d("No iteration logs found in %s, set to 0", mOutputFile);
    350                     iteration = Integer.valueOf(0);
    351                 }
    352                 runScanMetrics.put("wifi_scan_stress", iteration.toString());
    353             }
    354 
    355             // Report results
    356             reportMetrics(test.mTestMetricsName, listener, runMetrics);
    357             if (isScanningTest) {
    358                 reportMetrics("wifi_stress", listener, runScanMetrics);
    359             }
    360         } catch (IOException e) {
    361             CLog.e("IOException while reading from data stream");
    362             CLog.e(e);
    363             return;
    364         } finally {
    365             StreamUtil.close(br);
    366         }
    367     }
    368 
    369     /**
    370      * Report run metrics by creating an empty test run to stick them in
    371      * <p />
    372      * Exposed for unit testing
    373      */
    374     private void reportMetrics(String metricsName, ITestInvocationListener listener,
    375             Map<String, String> metrics) {
    376         // Create an empty testRun to report the parsed runMetrics
    377         CLog.d("About to report metrics to %s: %s", metricsName, metrics);
    378         listener.testRunStarted(metricsName, 0);
    379         listener.testRunEnded(0, metrics);
    380     }
    381 
    382     /**
    383      * Clean up output files from the last test run
    384      */
    385     private void cleanOutputFiles() throws DeviceNotAvailableException {
    386         CLog.d("Remove output file: %s", mOutputFile);
    387         String extStore = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE);
    388         mTestDevice.executeShellCommand(String.format("rm %s/%s", extStore, mOutputFile));
    389     }
    390 }
    391