blob: 23d758bb6d4bbb110ff41d8fd8bbf856da7352c3 [file] [log] [blame]
/*
* 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));
}
}