checkFrameworkSupport improvements for accuracy
Add retry and enforce the expected outputs.
Bug: 30558560
Change-Id: I70254bebe9f0387670f4a0d0060fb57215475f0f
diff --git a/remote/src/com/android/tradefed/device/DeviceAllocationEventHandler.java b/remote/src/com/android/tradefed/device/DeviceAllocationEventHandler.java
index 382ac8d..9121098 100644
--- a/remote/src/com/android/tradefed/device/DeviceAllocationEventHandler.java
+++ b/remote/src/com/android/tradefed/device/DeviceAllocationEventHandler.java
@@ -68,6 +68,8 @@
* <li>Checking_Availability -> AVAILABLE_CHECK_FAILED -> Unavailable</li>
* <li>Checking_Availability -> AVAILABLE_CHECK_IGNORED -> Ignored</li>
* <li>Checking_Availability -> FORCE_AVAILABLE -> Available</li>
+ * <li>Checking_Availability -> STATE_CHANGE_OFFLINE -> Unavailable</li>
+ * <li>Checking_Availability -> DISCONNECTED -> Unavailable</li>
* </ul>
*/
class CheckingAvailHandler implements DeviceAllocationEventHandler {
@@ -84,6 +86,10 @@
return DeviceAllocationState.Ignored;
case FORCE_AVAILABLE:
return DeviceAllocationState.Available;
+ case STATE_CHANGE_OFFLINE:
+ return DeviceAllocationState.Unavailable;
+ case DISCONNECTED:
+ return DeviceAllocationState.Unavailable;
default:
return DeviceAllocationState.Checking_Availability;
}
diff --git a/src/com/android/tradefed/device/DeviceManager.java b/src/com/android/tradefed/device/DeviceManager.java
index 95d9bd3..3178d50 100644
--- a/src/com/android/tradefed/device/DeviceManager.java
+++ b/src/com/android/tradefed/device/DeviceManager.java
@@ -306,7 +306,8 @@
Runnable checkRunnable = new Runnable() {
@Override
public void run() {
- CLog.d("checking new device %s responsiveness", testDevice.getSerialNumber());
+ CLog.d("checking new '%s' '%s' responsiveness", testDevice.getClass().getName(),
+ testDevice.getSerialNumber());
if (testDevice.getMonitor().waitForDeviceShell(CHECK_WAIT_DEVICE_AVAIL_MS)) {
DeviceEventResponse r = mManagedDeviceList.handleDeviceEvent(testDevice,
DeviceEvent.AVAILABLE_CHECK_PASSED);
diff --git a/src/com/android/tradefed/device/ManagedTestDeviceFactory.java b/src/com/android/tradefed/device/ManagedTestDeviceFactory.java
index 26cd728..2ee5e1b 100644
--- a/src/com/android/tradefed/device/ManagedTestDeviceFactory.java
+++ b/src/com/android/tradefed/device/ManagedTestDeviceFactory.java
@@ -22,6 +22,8 @@
import com.android.ddmlib.TimeoutException;
import com.android.tradefed.device.DeviceManager.FastbootDevice;
import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.IRunUtil;
+import com.android.tradefed.util.RunUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@@ -44,6 +46,9 @@
protected IDeviceMonitor mAllocationMonitor;
protected static final String CHECK_PM_CMD = "ls %s";
protected static final String EXPECTED_RES = "/system/bin/pm";
+ protected static final String EXPECTED_ERROR = "No such file or directory";
+ protected static final long FRAMEWORK_CHECK_SLEEP_MS = 500;
+ protected static final int FRAMEWORK_CHECK_MAX_RETRY = 3;
public ManagedTestDeviceFactory(boolean fastbootEnabled, IDeviceManager deviceManager,
IDeviceMonitor allocationMonitor) {
@@ -96,20 +101,32 @@
return true;
}
final long timeout = 60 * 1000;
- CollectingOutputReceiver receiver = createOutputReceiver();
try {
- if (!DeviceState.ONLINE.equals(idevice.getState())) {
- // Device will be 'unavailable' and recreated in DeviceManager so no need to check.
- CLog.w("Device state is not Online, assuming Framework support for now.");
- return true;
+ for (int i = 0; i < FRAMEWORK_CHECK_MAX_RETRY; i++) {
+ CollectingOutputReceiver receiver = createOutputReceiver();
+ if (!DeviceState.ONLINE.equals(idevice.getState())) {
+ // Device will be 'unavailable' and recreated in DeviceManager so no need to
+ // check.
+ CLog.w("Device state is not Online, assuming Framework support for now.");
+ return true;
+ }
+ String cmd = String.format(CHECK_PM_CMD, EXPECTED_RES);
+ idevice.executeShellCommand(cmd, receiver, timeout, TimeUnit.MILLISECONDS);
+ String output = receiver.getOutput().trim();
+ // It can only be one of the expected output or an exception if device offline
+ // otherwise we retry
+ if (EXPECTED_RES.equals(output)) {
+ return true;
+ }
+ if (output.contains(EXPECTED_ERROR)) {
+ CLog.i("No support for Framework, creating a native device. "
+ + "output: %s", receiver.getOutput());
+ return false;
+ }
+ getRunUtil().sleep(FRAMEWORK_CHECK_SLEEP_MS);
}
- String cmd = String.format(CHECK_PM_CMD, EXPECTED_RES);
- idevice.executeShellCommand(cmd, receiver, timeout, TimeUnit.MILLISECONDS);
- if (!EXPECTED_RES.equals(receiver.getOutput().trim())) {
- CLog.i("No support for Framework, creating a native device. "
- + "output: %s", receiver.getOutput());
- return false;
- }
+ CLog.w("Could not determine the framework support for '%s' after retries, assuming "
+ + "framework support.", idevice.getSerialNumber());
} catch (TimeoutException | AdbCommandRejectedException | ShellCommandUnresponsiveException
| IOException e) {
CLog.w("Exception during checkFrameworkSupport, assuming True: '%s' with device: %s",
@@ -121,6 +138,14 @@
}
/**
+ * Return the default {@link IRunUtil} instance.
+ * Exposed for testing.
+ */
+ protected IRunUtil getRunUtil() {
+ return RunUtil.getDefault();
+ }
+
+ /**
* Create a {@link CollectingOutputReceiver}.
* Exposed for testing.
*/
diff --git a/tests/src/com/android/tradefed/device/DeviceManagerTest.java b/tests/src/com/android/tradefed/device/DeviceManagerTest.java
index 1b7cbcf..b4d3db0 100644
--- a/tests/src/com/android/tradefed/device/DeviceManagerTest.java
+++ b/tests/src/com/android/tradefed/device/DeviceManagerTest.java
@@ -36,7 +36,6 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
-import java.util.concurrent.TimeUnit;
/**
* Unit tests for {@link DeviceManager}.
@@ -146,13 +145,21 @@
mMockRunUtil = EasyMock.createMock(IRunUtil.class);
mMockTestDevice = EasyMock.createMock(IManagedTestDevice.class);
- mMockDeviceFactory = new IManagedTestDeviceFactory() {
+ mMockDeviceFactory = new ManagedTestDeviceFactory(false, null, null) {
@Override
public IManagedTestDevice createDevice(IDevice idevice) {
mMockTestDevice.setIDevice(idevice);
return mMockTestDevice;
}
-
+ @Override
+ protected CollectingOutputReceiver createOutputReceiver() {
+ return new CollectingOutputReceiver() {
+ @Override
+ public String getOutput() {
+ return "/system/bin/pm";
+ }
+ };
+ }
@Override
public void setFastbootEnabled(boolean enable) {
// ignore
@@ -500,19 +507,24 @@
* with a global exclusion filter
*/
public void testInit_excludeDevice() throws Exception {
- String expectedCmd = String.format(ManagedTestDeviceFactory.CHECK_PM_CMD,
- ManagedTestDeviceFactory.EXPECTED_RES);
- EasyMock.expect(mMockIDevice.getState()).andReturn(DeviceState.ONLINE).times(3);
- mMockIDevice.executeShellCommand(EasyMock.eq(expectedCmd),
- (CollectingOutputReceiver)EasyMock.anyObject(), EasyMock.eq(60000l),
- EasyMock.eq(TimeUnit.MILLISECONDS));
+ EasyMock.expect(mMockIDevice.getState()).andReturn(DeviceState.ONLINE);
+ mMockTestDevice.setDeviceState(TestDeviceState.ONLINE);
EasyMock.expectLastCall();
+ DeviceEventResponse der =
+ new DeviceEventResponse(DeviceAllocationState.Checking_Availability, true);
+ EasyMock.expect(mMockTestDevice.handleAllocationEvent(DeviceEvent.CONNECTED_ONLINE))
+ .andReturn(der);
+ EasyMock.expect(mMockTestDevice.handleAllocationEvent(DeviceEvent.AVAILABLE_CHECK_IGNORED))
+ .andReturn(null);
+ EasyMock.expect(mMockTestDevice.handleAllocationEvent(DeviceEvent.ALLOCATE_REQUEST))
+ .andReturn(new DeviceEventResponse(DeviceAllocationState.Ignored, false));
replayMocks();
DeviceManager manager = createDeviceManagerNoInit();
DeviceSelectionOptions excludeFilter = new DeviceSelectionOptions();
excludeFilter.addExcludeSerial(mMockIDevice.getSerialNumber());
- manager.init(excludeFilter, null);
+ manager.init(excludeFilter, null, mMockDeviceFactory);
mDeviceListener.deviceConnected(mMockIDevice);
+ assertEquals(1, manager.getDeviceList().size());
assertNull(manager.allocateDevice());
verifyMocks();
}
@@ -526,16 +538,22 @@
EasyMock.expect(excludedDevice.getState()).andStubReturn(DeviceState.ONLINE);
EasyMock.expect(mMockIDevice.getState()).andStubReturn(DeviceState.ONLINE);
EasyMock.expect(excludedDevice.isEmulator()).andStubReturn(Boolean.FALSE);
- excludedDevice.executeShellCommand((String)EasyMock.anyObject(),
- (CollectingOutputReceiver)EasyMock.anyObject(), EasyMock.eq(60000l),
- EasyMock.eq(TimeUnit.MILLISECONDS));
- EasyMock.expectLastCall();
+ mMockTestDevice.setDeviceState(TestDeviceState.ONLINE);
+ DeviceEventResponse der =
+ new DeviceEventResponse(DeviceAllocationState.Checking_Availability, true);
+ EasyMock.expect(mMockTestDevice.handleAllocationEvent(DeviceEvent.CONNECTED_ONLINE))
+ .andReturn(der);
+ EasyMock.expect(mMockTestDevice.handleAllocationEvent(DeviceEvent.AVAILABLE_CHECK_IGNORED))
+ .andReturn(null);
+ EasyMock.expect(mMockTestDevice.handleAllocationEvent(DeviceEvent.ALLOCATE_REQUEST))
+ .andReturn(new DeviceEventResponse(DeviceAllocationState.Ignored, false));
replayMocks(excludedDevice);
DeviceManager manager = createDeviceManagerNoInit();
DeviceSelectionOptions includeFilter = new DeviceSelectionOptions();
includeFilter.addSerial(mMockIDevice.getSerialNumber());
- manager.init(includeFilter, null);
+ manager.init(includeFilter, null, mMockDeviceFactory);
mDeviceListener.deviceConnected(excludedDevice);
+ assertEquals(1, manager.getDeviceList().size());
// ensure excludedDevice cannot be allocated
assertNull(manager.allocateDevice());
verifyMocks(excludedDevice);
diff --git a/tests/src/com/android/tradefed/device/ManagedTestDeviceFactoryTest.java b/tests/src/com/android/tradefed/device/ManagedTestDeviceFactoryTest.java
index ec63181..a06c606 100644
--- a/tests/src/com/android/tradefed/device/ManagedTestDeviceFactoryTest.java
+++ b/tests/src/com/android/tradefed/device/ManagedTestDeviceFactoryTest.java
@@ -17,6 +17,7 @@
import com.android.ddmlib.IDevice;
import com.android.ddmlib.IDevice.DeviceState;
+import com.android.tradefed.util.IRunUtil;
import junit.framework.TestCase;
@@ -32,6 +33,7 @@
ManagedTestDeviceFactory mFactory;
IDeviceManager mMockDeviceManager;
IDeviceMonitor mMockDeviceMonitor;
+ IRunUtil mMockRunUtil;
@Override
protected void setUp() throws Exception {
@@ -39,6 +41,7 @@
mMockDeviceManager = EasyMock.createMock(IDeviceManager.class);
mMockDeviceMonitor = EasyMock.createMock(IDeviceMonitor.class);
mFactory = new ManagedTestDeviceFactory(true, mMockDeviceManager, mMockDeviceMonitor) ;
+ mMockRunUtil = EasyMock.createMock(IRunUtil.class);
}
public void testIsSerialTcpDevice() {
@@ -117,4 +120,37 @@
assertFalse(mFactory.checkFrameworkSupport(mMockDevice));
EasyMock.verify(mMockDevice);
}
+
+ /**
+ * Test that {@link ManagedTestDeviceFactory#checkFrameworkSupport(IDevice)} is retrying
+ * because device doesn't return a proper answer. It should return True for default value.
+ */
+ public void testCheckFramework_emptyReturns() throws Exception {
+ final CollectingOutputReceiver cor = new CollectingOutputReceiver();
+ mFactory = new ManagedTestDeviceFactory(true, mMockDeviceManager, mMockDeviceMonitor) {
+ @Override
+ protected CollectingOutputReceiver createOutputReceiver() {
+ String response = "";
+ cor.addOutput(response.getBytes(), 0, response.length());
+ return cor;
+ }
+ @Override
+ protected IRunUtil getRunUtil() {
+ return mMockRunUtil;
+ }
+ };
+ mMockRunUtil.sleep(500);
+ EasyMock.expectLastCall().times(ManagedTestDeviceFactory.FRAMEWORK_CHECK_MAX_RETRY);
+ IDevice mMockDevice = EasyMock.createMock(IDevice.class);
+ EasyMock.expect(mMockDevice.getSerialNumber()).andStubReturn("SERIAL");
+ EasyMock.expect(mMockDevice.getState()).andStubReturn(DeviceState.ONLINE);
+ String expectedCmd = String.format(ManagedTestDeviceFactory.CHECK_PM_CMD,
+ ManagedTestDeviceFactory.EXPECTED_RES);
+ mMockDevice.executeShellCommand(EasyMock.eq(expectedCmd), EasyMock.eq(cor),
+ EasyMock.anyLong(), EasyMock.eq(TimeUnit.MILLISECONDS));
+ EasyMock.expectLastCall().times(ManagedTestDeviceFactory.FRAMEWORK_CHECK_MAX_RETRY);
+ EasyMock.replay(mMockDevice, mMockRunUtil);
+ assertTrue(mFactory.checkFrameworkSupport(mMockDevice));
+ EasyMock.verify(mMockDevice, mMockRunUtil);
+ }
}