| /* |
| * 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.IDevice; |
| 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.FileInputStreamSource; |
| import com.android.tradefed.result.ITestInvocationListener; |
| import com.android.tradefed.result.InputStreamSource; |
| import com.android.tradefed.result.LogDataType; |
| 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.RunUtil; |
| import com.android.tradefed.util.StreamUtil; |
| import com.android.tradefed.util.proto.TfMetricProtoUtil; |
| |
| import org.junit.Assert; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| 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.concurrent.TimeUnit; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * Run the WiFi stress tests. This test stresses WiFi soft ap, WiFi scanning |
| * and WiFi reconnection in which device switches between cellular and WiFi connection. |
| */ |
| public class WifiStressTest implements IRemoteTest, IDeviceTest { |
| private ITestDevice mTestDevice = null; |
| private static final long START_TIMER = 5 * 60 * 1000; //5 minutes |
| // Define instrumentation test package and runner. |
| private static final String TEST_PACKAGE_NAME = "com.android.connectivitymanagertest"; |
| private static final String TEST_RUNNER_NAME = ".ConnectivityManagerStressTestRunner"; |
| |
| private static final Pattern ITERATION_PATTERN = |
| Pattern.compile("^iteration (\\d+) out of (\\d+)"); |
| private static final int AP_TEST_TIMER = 3 * 60 * 60 * 1000; // 3 hours |
| private static final int SCAN_TEST_TIMER = 30 * 60 * 1000; // 30 minutes |
| private static final int RECONNECT_TEST_TIMER = 12 * 60 * 60 * 1000; // 12 hours |
| |
| private String mOutputFile = "WifiStressTestOutput.txt"; |
| |
| /** |
| * Stores the test cases that we should consider running. |
| * <p/> |
| * This currently consists of "ap", "scanning", and "reconnection" tests. |
| */ |
| private List<TestInfo> mTestList = null; |
| |
| private static class TestInfo { |
| public String mTestName = null; |
| public String mTestClass = null; |
| public String mTestMethod = null; |
| public String mTestMetricsName = null; |
| public int mTestTimer; |
| public RegexTrie<String> mPatternMap = null; |
| |
| @Override |
| public String toString() { |
| return String.format("TestInfo: mTestName(%s), mTestClass(%s), mTestMethod(%s)," + |
| " mTestMetricsName(%s), mPatternMap(%s), mTestTimer(%d)", mTestName, |
| mTestClass, mTestMethod, mTestMetricsName, mPatternMap.toString(), mTestTimer); |
| } |
| } |
| |
| @Option( |
| name = "ap-iteration", |
| description = "The number of iterations to run soft ap stress test" |
| ) |
| private String mApIteration = "0"; |
| |
| @Option(name="idle-time", |
| description="The device idle time after screen off") |
| private String mIdleTime = "30"; // 30 seconds |
| |
| @Option(name="reconnect-iteration", |
| description="The number of iterations to run WiFi reconnection stress test") |
| private String mReconnectionIteration = "100"; |
| |
| @Option(name="reconnect-password", |
| description="The password for the above ssid in WiFi reconnection stress test") |
| private String mReconnectionPassword = "androidwifi"; |
| |
| @Option(name="reconnect-ssid", |
| description="The ssid for WiFi recoonection stress test") |
| private String mReconnectionSsid = "securenetdhcp"; |
| |
| @Option(name="reconnection-test", |
| description="Option to run the wifi reconnection stress test") |
| private boolean mReconnectionTestFlag = true; |
| |
| @Option(name="scan-iteration", |
| description="The number of iterations to run WiFi scanning test") |
| private String mScanIteration = "100"; |
| |
| @Option(name="scan-test", |
| description="Option to run the scan stress test") |
| private boolean mScanTestFlag = true; |
| |
| @Option(name="skip-set-device-screen-timeout", |
| description="Option to skip screen timeout configuration") |
| private boolean mSkipSetDeviceScreenTimeout = false; |
| |
| @Option(name="tether-test", |
| description="Option to run the tethering stress test") |
| private boolean mTetherTestFlag = true; |
| |
| @Option(name="wifi-only") |
| private boolean mWifiOnly = false; |
| |
| private void setupTests() { |
| if (mTestList != null) { |
| return; |
| } |
| mTestList = new ArrayList<>(3); |
| |
| // Add WiFi scanning test |
| TestInfo t = new TestInfo(); |
| t.mTestName = "WifiScanning"; |
| t.mTestClass = "com.android.connectivitymanagertest.stress.WifiStressTest"; |
| t.mTestMethod = "testWifiScanning"; |
| t.mTestMetricsName = "wifi_scan_performance"; |
| t.mTestTimer = SCAN_TEST_TIMER; |
| t.mPatternMap = new RegexTrie<>(); |
| t.mPatternMap.put("avg_scan_time", "^average scanning time is (\\d+)"); |
| t.mPatternMap.put("scan_quality","ssid appear (\\d+) out of (\\d+) scan iterations"); |
| if (mScanTestFlag) { |
| mTestList.add(t); |
| } |
| |
| // Add WiFi reconnection test |
| t = new TestInfo(); |
| t.mTestName = "WifiReconnectionStress"; |
| t.mTestClass = "com.android.connectivitymanagertest.stress.WifiStressTest"; |
| t.mTestMethod = "testWifiReconnectionAfterSleep"; |
| t.mTestMetricsName = "wifi_stress"; |
| t.mTestTimer = RECONNECT_TEST_TIMER; |
| t.mPatternMap = new RegexTrie<>(); |
| t.mPatternMap.put("wifi_reconnection_stress", ITERATION_PATTERN); |
| if (mReconnectionTestFlag) { |
| mTestList.add(t); |
| } |
| } |
| |
| /** |
| * Configure screen timeout property |
| * @throws DeviceNotAvailableException |
| */ |
| private void setDeviceScreenTimeout() throws DeviceNotAvailableException { |
| // Set device screen_off_timeout as svc power can be set to false in the Wi-Fi test |
| String command = ("sqlite3 /data/data/com.android.providers.settings/databases/settings.db " |
| + "\"UPDATE system SET value=\'600000\' WHERE name=\'screen_off_timeout\';\""); |
| CLog.d("Command to set screen timeout value to 10 minutes: %s", command); |
| mTestDevice.executeShellCommand(command); |
| |
| // reboot to allow the setting to take effect, post setup will be taken care by the reboot |
| mTestDevice.reboot(); |
| } |
| |
| /** |
| * Enable/disable screen never timeout property |
| * @param on |
| * @throws DeviceNotAvailableException |
| */ |
| private void setScreenProperty(boolean on) throws DeviceNotAvailableException { |
| CLog.d("set svc power stay on " + on); |
| mTestDevice.executeShellCommand("svc power stayon " + on); |
| } |
| |
| @Override |
| public void setDevice(ITestDevice testDevice) { |
| mTestDevice = testDevice; |
| } |
| |
| @Override |
| public ITestDevice getDevice() { |
| return mTestDevice; |
| } |
| |
| /** |
| * Run the Wi-Fi stress test |
| * Collect results and post results to dashboard |
| */ |
| @Override |
| public void run(ITestInvocationListener standardListener) |
| throws DeviceNotAvailableException { |
| Assert.assertNotNull(mTestDevice); |
| setupTests(); |
| if (!mSkipSetDeviceScreenTimeout) { |
| setDeviceScreenTimeout(); |
| } |
| RunUtil.getDefault().sleep(START_TIMER); |
| |
| if (!mWifiOnly) { |
| final RadioHelper radioHelper = new RadioHelper(mTestDevice); |
| Assert.assertTrue("Radio activation failed", radioHelper.radioActivation()); |
| Assert.assertTrue("Data setup failed", radioHelper.waitForDataSetup()); |
| } |
| |
| IRemoteAndroidTestRunner runner = new RemoteAndroidTestRunner( |
| TEST_PACKAGE_NAME, TEST_RUNNER_NAME, mTestDevice.getIDevice()); |
| runner.addInstrumentationArg("softap_iterations", mApIteration); |
| runner.addInstrumentationArg("scan_iterations", mScanIteration); |
| runner.addInstrumentationArg("reconnect_iterations", mReconnectionIteration); |
| runner.addInstrumentationArg("reconnect_ssid", mReconnectionSsid); |
| runner.addInstrumentationArg("reconnect_password", mReconnectionPassword); |
| runner.addInstrumentationArg("sleep_time", mIdleTime); |
| if (mWifiOnly) { |
| runner.addInstrumentationArg("wifi-only", String.valueOf(mWifiOnly)); |
| } |
| |
| // Add bugreport listener for failed test |
| BugreportCollector bugListener = new |
| BugreportCollector(standardListener, mTestDevice); |
| bugListener.addPredicate(BugreportCollector.AFTER_FAILED_TESTCASES); |
| // 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); |
| |
| for (TestInfo testCase : mTestList) { |
| // for Wi-Fi reconnection test, |
| if ("WifiReconnectionStress".equals(testCase.mTestName)) { |
| setScreenProperty(false); |
| } else { |
| setScreenProperty(true); |
| } |
| CLog.d("TestInfo: " + testCase.toString()); |
| runner.setClassName(testCase.mTestClass); |
| runner.setMethodName(testCase.mTestClass, testCase.mTestMethod); |
| runner.setMaxTimeToOutputResponse(testCase.mTestTimer, TimeUnit.MILLISECONDS); |
| bugListener.setDescriptiveName(testCase.mTestName); |
| mTestDevice.runInstrumentationTests(runner, bugListener); |
| logOutputFile(testCase, bugListener); |
| cleanOutputFiles(); |
| } |
| } |
| |
| /** |
| * Collect test results, report test results to dash board. |
| * |
| * @param test |
| * @param listener |
| */ |
| private void logOutputFile(TestInfo test, ITestInvocationListener listener) |
| throws DeviceNotAvailableException { |
| File resFile = null; |
| InputStreamSource outputSource = null; |
| |
| try { |
| resFile = mTestDevice.pullFileFromExternal(mOutputFile); |
| if (resFile != null) { |
| // Save a copy of the output file |
| CLog.d("Sending %d byte file %s into the logosphere!", |
| resFile.length(), resFile); |
| outputSource = new FileInputStreamSource(resFile); |
| listener.testLog(String.format("result-%s.txt", test.mTestName), LogDataType.TEXT, |
| outputSource); |
| |
| // Parse the results file and post results to test listener |
| parseOutputFile(test, resFile, listener); |
| } |
| } finally { |
| FileUtil.deleteFile(resFile); |
| StreamUtil.cancel(outputSource); |
| } |
| } |
| |
| private void parseOutputFile(TestInfo test, File dataFile, |
| ITestInvocationListener listener) { |
| Map<String, String> runMetrics = new HashMap<>(); |
| Map<String, String> runScanMetrics = null; |
| boolean isScanningTest = "WifiScanning".equals(test.mTestName); |
| Integer iteration = null; |
| BufferedReader br = null; |
| try { |
| br = new BufferedReader(new FileReader(dataFile)); |
| String line = null; |
| while ((line = br.readLine()) != null) { |
| List<List<String>> capture = new ArrayList<>(1); |
| String key = test.mPatternMap.retrieve(capture, line); |
| if (key != null) { |
| CLog.d("In output file of test case %s: retrieve key: %s, " + |
| "catpure: %s", test.mTestName, key, capture.toString()); |
| //Save results in the metrics |
| if ("scan_quality".equals(key)) { |
| // For scanning test, calculate the scan quality |
| int count = Integer.parseInt(capture.get(0).get(0)); |
| int total = Integer.parseInt(capture.get(0).get(1)); |
| int quality = 0; |
| if (total != 0) { |
| quality = (100 * count) / total; |
| } |
| runMetrics.put(key, Integer.toString(quality)); |
| } else { |
| runMetrics.put(key, capture.get(0).get(0)); |
| } |
| } else { |
| // For scanning test, iterations will also be counted. |
| if (isScanningTest) { |
| Matcher m = ITERATION_PATTERN.matcher(line); |
| if (m.matches()) { |
| iteration = Integer.parseInt(m.group(1)); |
| } |
| } |
| } |
| } |
| if (isScanningTest) { |
| runScanMetrics = new HashMap<>(1); |
| if (iteration == null) { |
| // no matching is found |
| CLog.d("No iteration logs found in %s, set to 0", mOutputFile); |
| iteration = Integer.valueOf(0); |
| } |
| runScanMetrics.put("wifi_scan_stress", iteration.toString()); |
| } |
| |
| // Report results |
| reportMetrics(test.mTestMetricsName, listener, runMetrics); |
| if (isScanningTest) { |
| reportMetrics("wifi_stress", listener, runScanMetrics); |
| } |
| } catch (IOException e) { |
| CLog.e("IOException while reading from data stream"); |
| CLog.e(e); |
| return; |
| } finally { |
| StreamUtil.close(br); |
| } |
| } |
| |
| /** |
| * Report run metrics by creating an empty test run to stick them in |
| * <p /> |
| * Exposed for unit testing |
| */ |
| 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, TfMetricProtoUtil.upgradeConvert(metrics)); |
| } |
| |
| /** |
| * Clean up output files from the last test run |
| */ |
| private void cleanOutputFiles() throws DeviceNotAvailableException { |
| CLog.d("Remove output file: %s", mOutputFile); |
| String extStore = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); |
| mTestDevice.executeShellCommand(String.format("rm %s/%s", extStore, mOutputFile)); |
| } |
| } |