1 /* 2 * Copyright (C) 2010 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 17 package com.android.tradefed.targetprep; 18 19 import static org.junit.Assert.assertFalse; 20 import static org.junit.Assert.assertNull; 21 import static org.junit.Assert.assertTrue; 22 import static org.junit.Assert.fail; 23 24 import com.android.tradefed.build.DeviceBuildInfo; 25 import com.android.tradefed.build.IBuildInfo; 26 import com.android.tradefed.build.IDeviceBuildInfo; 27 import com.android.tradefed.command.remote.DeviceDescriptor; 28 import com.android.tradefed.device.DeviceAllocationState; 29 import com.android.tradefed.device.DeviceNotAvailableException; 30 import com.android.tradefed.device.DeviceUnresponsiveException; 31 import com.android.tradefed.device.ITestDevice; 32 import com.android.tradefed.device.ITestDevice.RecoveryMode; 33 import com.android.tradefed.device.TestDeviceOptions; 34 import com.android.tradefed.host.IHostOptions; 35 import com.android.tradefed.targetprep.IDeviceFlasher.UserDataFlashOption; 36 import com.android.tradefed.util.FileUtil; 37 import com.android.tradefed.util.RunUtil; 38 39 import org.easymock.EasyMock; 40 import org.junit.After; 41 import org.junit.Before; 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 import org.junit.runners.JUnit4; 45 46 import java.io.File; 47 import java.util.Arrays; 48 import java.util.concurrent.Semaphore; 49 50 /** Unit tests for {@link DeviceFlashPreparer}. */ 51 @RunWith(JUnit4.class) 52 public class DeviceFlashPreparerTest { 53 54 private IDeviceFlasher mMockFlasher; 55 private DeviceFlashPreparer mDeviceFlashPreparer; 56 private ITestDevice mMockDevice; 57 private IDeviceBuildInfo mMockBuildInfo; 58 private IHostOptions mMockHostOptions; 59 private File mTmpDir; 60 61 @Before 62 public void setUp() throws Exception { 63 mMockFlasher = EasyMock.createMock(IDeviceFlasher.class); 64 mMockDevice = EasyMock.createMock(ITestDevice.class); 65 EasyMock.expect(mMockDevice.getSerialNumber()).andReturn("foo").anyTimes(); 66 EasyMock.expect(mMockDevice.getOptions()).andReturn(new TestDeviceOptions()).anyTimes(); 67 mMockBuildInfo = new DeviceBuildInfo("0", ""); 68 mMockBuildInfo.setDeviceImageFile(new File("foo"), "0"); 69 mMockBuildInfo.setBuildFlavor("flavor"); 70 mMockHostOptions = EasyMock.createMock(IHostOptions.class); 71 mDeviceFlashPreparer = new DeviceFlashPreparer() { 72 @Override 73 protected IDeviceFlasher createFlasher(ITestDevice device) { 74 return mMockFlasher; 75 } 76 77 @Override 78 int getDeviceBootPollTimeMs() { 79 return 100; 80 } 81 82 @Override 83 IHostOptions getHostOptions() { 84 return mMockHostOptions; 85 } 86 }; 87 // Reset default settings 88 mDeviceFlashPreparer.setConcurrentFlashSettings(null, null, true); 89 mDeviceFlashPreparer.setDeviceBootTime(100); 90 // expect this call 91 mMockFlasher.setUserDataFlashOption(UserDataFlashOption.FLASH); 92 mTmpDir = FileUtil.createTempDir("tmp"); 93 } 94 95 @After 96 public void tearDown() throws Exception { 97 FileUtil.recursiveDelete(mTmpDir); 98 } 99 100 /** Simple normal case test for {@link DeviceFlashPreparer#setUp(ITestDevice, IBuildInfo)}. */ 101 @Test 102 public void testSetup() throws Exception { 103 doSetupExpectations(); 104 EasyMock.replay(mMockFlasher, mMockDevice); 105 mDeviceFlashPreparer.setUp(mMockDevice, mMockBuildInfo); 106 EasyMock.verify(mMockFlasher, mMockDevice); 107 } 108 109 /** 110 * Set EasyMock expectations for a normal setup call 111 */ 112 private void doSetupExpectations() throws TargetSetupError, DeviceNotAvailableException { 113 mMockDevice.setRecoveryMode(RecoveryMode.ONLINE); 114 mMockFlasher.overrideDeviceOptions(mMockDevice); 115 mMockFlasher.setForceSystemFlash(false); 116 mMockFlasher.setDataWipeSkipList(Arrays.asList(new String[]{})); 117 mMockFlasher.flash(mMockDevice, mMockBuildInfo); 118 mMockFlasher.setWipeTimeout(EasyMock.anyLong()); 119 mMockDevice.waitForDeviceOnline(); 120 EasyMock.expect(mMockDevice.enableAdbRoot()).andStubReturn(Boolean.TRUE); 121 mMockDevice.setDate(null); 122 EasyMock.expect(mMockDevice.getBuildId()).andReturn(mMockBuildInfo.getBuildId()); 123 EasyMock.expect(mMockDevice.getBuildFlavor()).andReturn(mMockBuildInfo.getBuildFlavor()); 124 EasyMock.expect(mMockDevice.isEncryptionSupported()).andStubReturn(Boolean.TRUE); 125 EasyMock.expect(mMockDevice.isDeviceEncrypted()).andStubReturn(Boolean.FALSE); 126 mMockDevice.clearLogcat(); 127 mMockDevice.waitForDeviceAvailable(EasyMock.anyLong()); 128 mMockDevice.setRecoveryMode(RecoveryMode.AVAILABLE); 129 mMockDevice.postBootSetup(); 130 } 131 132 /** 133 * Test {@link DeviceFlashPreparer#setUp(ITestDevice, IBuildInfo)} when a non IDeviceBuildInfo 134 * type is provided. 135 */ 136 @Test 137 public void testSetUp_nonDevice() throws Exception { 138 try { 139 mDeviceFlashPreparer.setUp(mMockDevice, EasyMock.createMock(IBuildInfo.class)); 140 fail("IllegalArgumentException not thrown"); 141 } catch (IllegalArgumentException e) { 142 // expected 143 } 144 } 145 146 /** Test {@link DeviceFlashPreparer#setUp(ITestDevice, IBuildInfo)} when build does not boot. */ 147 @Test 148 public void testSetup_buildError() throws Exception { 149 mMockDevice.setRecoveryMode(RecoveryMode.ONLINE); 150 mMockFlasher.overrideDeviceOptions(mMockDevice); 151 mMockFlasher.setForceSystemFlash(false); 152 mMockFlasher.setDataWipeSkipList(Arrays.asList(new String[]{})); 153 mMockFlasher.flash(mMockDevice, mMockBuildInfo); 154 mMockFlasher.setWipeTimeout(EasyMock.anyLong()); 155 mMockDevice.waitForDeviceOnline(); 156 EasyMock.expect(mMockDevice.enableAdbRoot()).andStubReturn(Boolean.TRUE); 157 mMockDevice.setDate(null); 158 EasyMock.expect(mMockDevice.getBuildId()).andReturn(mMockBuildInfo.getBuildId()); 159 EasyMock.expect(mMockDevice.getBuildFlavor()).andReturn(mMockBuildInfo.getBuildFlavor()); 160 EasyMock.expect(mMockDevice.isEncryptionSupported()).andStubReturn(Boolean.TRUE); 161 EasyMock.expect(mMockDevice.isDeviceEncrypted()).andStubReturn(Boolean.FALSE); 162 mMockDevice.clearLogcat(); 163 mMockDevice.waitForDeviceAvailable(EasyMock.anyLong()); 164 EasyMock.expectLastCall().andThrow(new DeviceUnresponsiveException("foo", "fakeserial")); 165 mMockDevice.setRecoveryMode(RecoveryMode.AVAILABLE); 166 EasyMock.expect(mMockDevice.getDeviceDescriptor()).andReturn( 167 new DeviceDescriptor("SERIAL", false, DeviceAllocationState.Available, "unknown", 168 "unknown", "unknown", "unknown", "unknown")); 169 EasyMock.replay(mMockFlasher, mMockDevice); 170 try { 171 mDeviceFlashPreparer.setUp(mMockDevice, mMockBuildInfo); 172 fail("DeviceFlashPreparerTest not thrown"); 173 } catch (BuildError e) { 174 // expected; use the general version to make absolutely sure that 175 // DeviceFailedToBootError properly masquerades as a BuildError. 176 assertTrue(e instanceof DeviceFailedToBootError); 177 } 178 EasyMock.verify(mMockFlasher, mMockDevice); 179 } 180 181 /** Ensure that the flasher instance limiting machinery is working as expected. */ 182 @Test 183 public void testFlashLimit() throws Exception { 184 final DeviceFlashPreparer dfp = mDeviceFlashPreparer; 185 try { 186 Thread waiter = new Thread() { 187 @Override 188 public void run() { 189 dfp.takeFlashingPermit(); 190 dfp.returnFlashingPermit(); 191 } 192 }; 193 EasyMock.expect(mMockHostOptions.getConcurrentFlasherLimit()).andReturn(1).anyTimes(); 194 EasyMock.replay(mMockHostOptions); 195 dfp.setConcurrentFlashSettings(1, new Semaphore(1), true); 196 // take the permit; the next attempt to take the permit should block 197 dfp.takeFlashingPermit(); 198 assertFalse(dfp.getConcurrentFlashLock().hasQueuedThreads()); 199 200 waiter.start(); 201 RunUtil.getDefault().sleep(100); // Thread start should take <100ms 202 assertTrue("Invalid state: waiter thread is not alive", waiter.isAlive()); 203 assertTrue("No queued threads", dfp.getConcurrentFlashLock().hasQueuedThreads()); 204 205 dfp.returnFlashingPermit(); 206 RunUtil.getDefault().sleep(100); // Thread start should take <100ms 207 assertFalse("Unexpected queued threads", 208 dfp.getConcurrentFlashLock().hasQueuedThreads()); 209 210 waiter.join(1000); 211 assertFalse("waiter thread has not returned", waiter.isAlive()); 212 } finally { 213 // Attempt to reset concurrent flash settings to defaults 214 dfp.setConcurrentFlashSettings(null, null, true); 215 } 216 } 217 218 /** Ensure that the flasher limiting respects {@link IHostOptions}. */ 219 @Test 220 public void testFlashLimit_withHostOptions() throws Exception { 221 final DeviceFlashPreparer dfp = mDeviceFlashPreparer; 222 try { 223 Thread waiter = new Thread() { 224 @Override 225 public void run() { 226 dfp.takeFlashingPermit(); 227 dfp.returnFlashingPermit(); 228 } 229 }; 230 EasyMock.expect(mMockHostOptions.getConcurrentFlasherLimit()).andReturn(1).anyTimes(); 231 EasyMock.replay(mMockHostOptions); 232 // take the permit; the next attempt to take the permit should block 233 dfp.takeFlashingPermit(); 234 assertFalse(dfp.getConcurrentFlashLock().hasQueuedThreads()); 235 236 waiter.start(); 237 RunUtil.getDefault().sleep(100); // Thread start should take <100ms 238 assertTrue("Invalid state: waiter thread is not alive", waiter.isAlive()); 239 assertTrue("No queued threads", dfp.getConcurrentFlashLock().hasQueuedThreads()); 240 241 dfp.returnFlashingPermit(); 242 RunUtil.getDefault().sleep(100); // Thread start should take <100ms 243 assertFalse("Unexpected queued threads", 244 dfp.getConcurrentFlashLock().hasQueuedThreads()); 245 246 waiter.join(1000); 247 assertFalse("waiter thread has not returned", waiter.isAlive()); 248 EasyMock.verify(mMockHostOptions); 249 } finally { 250 // Attempt to reset concurrent flash settings to defaults 251 dfp.setConcurrentFlashSettings(null, null, true); 252 } 253 } 254 255 /** Ensure that the flasher instance limiting machinery is working as expected. */ 256 @Test 257 public void testUnlimitedFlashLimit() throws Exception { 258 final DeviceFlashPreparer dfp = mDeviceFlashPreparer; 259 try { 260 Thread waiter = new Thread() { 261 @Override 262 public void run() { 263 dfp.takeFlashingPermit(); 264 dfp.returnFlashingPermit(); 265 } 266 }; 267 dfp.setConcurrentFlashSettings(null, null, true); 268 // take a permit; the next attempt to take the permit should proceed without blocking 269 dfp.takeFlashingPermit(); 270 assertNull("Flash lock is non-null", dfp.getConcurrentFlashLock()); 271 272 waiter.start(); 273 RunUtil.getDefault().sleep(100); // Thread start should take <100ms 274 Thread.State waiterState = waiter.getState(); 275 assertTrue("Invalid state: waiter thread hasn't started", 276 waiter.isAlive() || Thread.State.TERMINATED.equals(waiterState)); 277 assertNull("Flash lock is non-null", dfp.getConcurrentFlashLock()); 278 279 dfp.returnFlashingPermit(); 280 RunUtil.getDefault().sleep(100); // Thread start should take <100ms 281 assertNull("Flash lock is non-null", dfp.getConcurrentFlashLock()); 282 283 waiter.join(1000); 284 assertFalse("waiter thread has not returned", waiter.isAlive()); 285 } finally { 286 // Attempt to reset concurrent flash settings to defaults 287 dfp.setConcurrentFlashSettings(null, null, true); 288 } 289 } 290 } 291